forked from 1b-agent/opencadstudio-upstream
fix(blocks): prevent circular block nesting and cut render cycles
Introduce would_create_cycle in block_service to prevent recursive block definitions with case-insensitive matching across block records. Enforce cycle refusal at INSERT commit in BEDIT and during block redefinition (redefine_block_raw) with a shared error message. Ensure render-time recursion truncates safely with a one-time session warning on cyclic documents.
This commit is contained in:
1 parent
59bb6faabd
commit
9c08e8a3ed
8 files changed
+435
-24
No files matched your search
@@ -1519,3 +1519,47 @@ fn locked_attedit_attribute_flags_cases() {
|
||||
));
|
||||
assert_eq!(preset_entity.get_value(), "edited_preset");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_insert_refuses_cycle() {
|
||||
let mut app = app();
|
||||
let name_a = "BEDIT_CYC_A";
|
||||
let name_b = "BEDIT_CYC_B";
|
||||
|
||||
define_block(&mut app, name_a);
|
||||
|
||||
// B contains an insert of A
|
||||
let ins_a = Insert::new(name_a.to_string(), Vector3::ZERO);
|
||||
let i = tab(&app);
|
||||
app.tabs[i]
|
||||
.scene
|
||||
.define_block_raw(name_b, Vector3::ZERO, vec![EntityType::Insert(ins_a)]);
|
||||
|
||||
// Now open BEDIT for block A
|
||||
let insert_a = add_insert(&mut app, name_a);
|
||||
begin_bedit(&mut app, insert_a);
|
||||
assert!(bedit_open(&app));
|
||||
|
||||
// Try committing an INSERT of B into A inside BEDIT space:
|
||||
// Inserting B into A would create A -> B -> A cycle
|
||||
let _ = app.dispatch_command("-INSERT");
|
||||
let mut feed = |text: &str| {
|
||||
let _ = app.update(crate::app::Message::CommandInput(text.to_string()));
|
||||
let _ = app.update(crate::app::Message::CommandSubmit);
|
||||
};
|
||||
feed(name_b);
|
||||
feed("0,0");
|
||||
feed("1");
|
||||
feed("1");
|
||||
feed("0");
|
||||
|
||||
// Verify refusal error on command line
|
||||
let errs = errors(&app);
|
||||
assert!(
|
||||
errs.iter()
|
||||
.any(|e| e.contains("circular dependency detected")),
|
||||
"expected cycle error in errors: {errs:?}"
|
||||
);
|
||||
|
||||
let _ = app.dispatch_command("BEDIT_DISCARD");
|
||||
}
|
||||
@@ -427,7 +427,11 @@ impl OpenCADStudio {
|
||||
for def in deps.blocks {
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
if redefine && def.name.eq_ignore_ascii_case(name) {
|
||||
scene.redefine_block_raw(&def.name, def.base_point, def.entities);
|
||||
if let Err(err) = scene.redefine_block_raw(&def.name, def.base_point, def.entities)
|
||||
{
|
||||
self.command_line.push_error(&err);
|
||||
return Err(err);
|
||||
}
|
||||
} else if scene.document.block_records.get(&def.name).is_none() {
|
||||
scene.define_block_raw(&def.name, def.base_point, def.entities);
|
||||
}
|
||||
|
||||
@@ -580,11 +580,14 @@ impl OpenCADStudio {
|
||||
.define_block_raw(&def.name, def.base_point, def.entities);
|
||||
}
|
||||
if redefine {
|
||||
self.tabs[i].scene.redefine_block_raw(
|
||||
if let Err(err) = self.tabs[i].scene.redefine_block_raw(
|
||||
&name,
|
||||
codec::types::Vector3::new(base.x, base.y, base.z),
|
||||
entities,
|
||||
);
|
||||
) {
|
||||
self.command_line.push_error(&err);
|
||||
return Err(err);
|
||||
}
|
||||
} else {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
|
||||
Reference in new issue
Block a user