M1: swap defaults past the layer cap say so (#981)

Instance swap defaults that name an in-file main skipped by the layer cap
were reported as "isn't in the file": only instance targets fed the
capped-mains list. Defaults now feed it too.
This commit is contained in:
Ridzzz0Alam
2026-10-21 21:06:59 +02:00
parent e211bf5476
commit f0e75e183b
2 changed files with 34 additions and 8 deletions
+21
View File
@@ -103,6 +103,27 @@ fn instances_of_a_main_the_cap_kept_out_say_so() {
assert!(!doc.nodes().any(|n| n.name == "Local main"));
}
#[test]
fn swap_defaults_the_cap_kept_out_say_so() {
// A kept main's instance swap default names a main with no instances, which comes after
// enough layers to be skipped at the cap.
let swap = o(vec![
("id", g(70)),
("name", Kv::Str("Icon".into())),
("type", Kv::Enum("INSTANCE_SWAP")),
("initialValue", o(vec![("guidValue", g(900))])),
]);
let mut nodes = vec![node(1, None, "DOCUMENT", "Document", vec![]), node(2, Some(1), "CANVAS", "Page", vec![])];
nodes.push(node(50, Some(2), "SYMBOL", "Card", vec![("size", xy(10.0, 10.0)), ("componentPropDefs", Kv::Array(vec![swap]))]));
nodes.extend(main(60, 2, "Filler", 40));
nodes.extend(main(900, 2, "Icon main", 10));
let (doc, warnings) = import_at(&o(vec![("nodeChanges", Kv::Array(nodes))]), 30);
assert!(doc.nodes().any(|n| n.name == "Card"));
assert!(!doc.nodes().any(|n| n.name == "Icon main"));
assert!(warnings.iter().any(|w| w.starts_with("instance swap properties whose default component was past the layer limit")), "{warnings:?}");
assert!(!warnings.iter().any(|w| w.contains("isn't in the file")), "{warnings:?}");
}
#[test]
fn a_whole_file_past_the_cap_keeps_its_library_links_and_says_so_once() {
// A 1 001-layer library main used 210 times: about 210 000 layers in a file far too small to
+13 -8
View File
@@ -45,7 +45,7 @@ pub(super) fn to_rest(msg: &Kv, name: &str, max_nodes: usize) -> (Value, Vec<Str
// Mains in the file that the layer cap kept out: their instances are told apart from those of
// components the file doesn't have (#981).
if m.count >= m.max_nodes {
let capped: Vec<Value> = m.instance_mains.difference(&m.mapped_mains).map(|id| json!(id)).collect();
let capped: Vec<Value> = m.referenced_mains.difference(&m.mapped_mains).map(|id| json!(id)).collect();
if !capped.is_empty() {
out[crate::rest::FIG_CAPPED_MAINS_FIELD] = Value::Array(capped);
}
@@ -397,9 +397,9 @@ struct Mapper<'a> {
library_scan_count: usize,
warnings: Vec<String>,
count: usize,
/// The mains in the file that instances name, and the mains mapped as layers: the difference
/// is what the layer cap kept out.
instance_mains: BTreeSet<String>,
/// The mains in the file that instances or instance swap defaults name, and the mains mapped
/// as layers: the difference is what the layer cap kept out.
referenced_mains: BTreeSet<String>,
mapped_mains: BTreeSet<String>,
/// Most layers produced or scanned, instance expansion included; the one place the cap comes
/// from (see [`super::layer_budget`]).
@@ -478,7 +478,7 @@ impl<'a> Mapper<'a> {
library_scan_count: 0,
warnings: Vec::new(),
count: 0,
instance_mains: BTreeSet::new(),
referenced_mains: BTreeSet::new(),
mapped_mains: BTreeSet::new(),
max_nodes,
networks: Vec::new(),
@@ -1869,7 +1869,12 @@ impl<'a> Mapper<'a> {
"BOOL" => json!({"type": "BOOLEAN", "defaultValue": init.and_then(|i| i.get("boolValue")).and_then(Kv::bool).unwrap_or(true)}),
"TEXT" => json!({"type": "TEXT", "defaultValue": init.and_then(prop_text).unwrap_or("")}),
"INSTANCE_SWAP" => match init.and_then(|i| i.get("guidValue")).and_then(guid) {
Some(g) => json!({"type": "INSTANCE_SWAP", "defaultValue": g}),
Some(g) => {
if self.nodes.contains_key(&g) {
self.referenced_mains.insert(g.clone());
}
json!({"type": "INSTANCE_SWAP", "defaultValue": g})
}
None => continue,
},
"VARIANT" => json!({"type": "VARIANT"}),
@@ -2013,8 +2018,8 @@ impl<'a> Mapper<'a> {
self.warn("instances of components that aren't in the file show no layers");
return None;
}
if !self.instance_mains.contains(&sym) {
self.instance_mains.insert(sym.clone());
if !self.referenced_mains.contains(&sym) {
self.referenced_mains.insert(sym.clone());
}
if outer.is_some_and(|i| i.symbols.contains(&sym)) {
return None;