fix(types): trim class names in Node::add_class

The doc comment promised blank names were ignored, but the guard only
rejected the empty string. Stylesheet selectors match class names exactly,
so a padded name would sit in `classes` and match no rule.

No current caller can pass one: the parser splits on whitespace, and the
subgraph and import paths strip everything but alphanumerics and hyphens.
This makes the public contract on the shared type match what it claims.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-07-29 22:25:42 -04:00
parent 01c4a4a23b
commit 727cd52c3b
No known key found for this signature in database

View file

@ -155,9 +155,14 @@ impl Node {
/// enclosing subgraphs, and import placeholders — so every caller needs the
/// same de-duplicating append.
///
/// The name is trimmed, and a name that is empty or only whitespace is
/// dropped. Stylesheet selectors match class names exactly, so a padded
/// name would never match any rule.
///
/// Order is preserved because the first class is meaningful: it supplies
/// the fallback thread ID for fidelity threading.
pub fn add_class(&mut self, class: &str) {
let class = class.trim();
if !class.is_empty() && !self.classes.iter().any(|existing| existing == class) {
self.classes.push(class.to_string());
}
@ -684,6 +689,18 @@ mod tests {
assert!(node.project_memory());
}
#[test]
fn add_class_trims_names_and_drops_blanks_and_duplicates() {
let mut node = Node::new("work");
node.add_class("coding");
node.add_class(" coding ");
node.add_class("");
node.add_class(" ");
node.add_class("\tcritical\n");
assert_eq!(node.classes, ["coding", "critical"]);
}
fn node_with(id: &str, attrs: &[(&str, &str)]) -> Node {
let mut node = Node::new(id);
for (key, value) in attrs {