From 9f911963e9af8fbbe1390ec892ac520467743fb7 Mon Sep 17 00:00:00 2001 From: pcnofelt Date: Sat, 20 Jun 2026 22:01:07 -0700 Subject: [PATCH] ident: make Name's inner Vec private Make Name's inner Vec private so callers can't construct an empty Name and break the invariant that name helpers like first_label() rely on. FromStr already enforces this; the public tuple field was the only way to bypass it. Update the code that was still reaching into the tuple field to build names by parsing instead, refresh the interface doc to describe Name as opaque, and switch first_label() to the local assert() helper for this invariant. --- context/interfaces/src/ident.md | 5 +++-- src/ident.rs | 4 ++-- src/modules/adhoc.rs | 2 +- src/trust_engine_bench/node.rs | 16 +++++++--------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/context/interfaces/src/ident.md b/context/interfaces/src/ident.md index 7d90e60..a3bf7df 100644 --- a/context/interfaces/src/ident.md +++ b/context/interfaces/src/ident.md @@ -9,8 +9,9 @@ ## Public interface ```rust -/// Human-readable mesh name. -pub struct Name(pub Vec); +/// Human-readable mesh name. The label representation is private; construct +/// via `FromStr` so the non-empty invariant always holds. +pub struct Name; /// Name or wildcard authority pattern over mesh names. pub struct NamePattern; diff --git a/src/ident.rs b/src/ident.rs index f5f4bab..d2b84e1 100644 --- a/src/ident.rs +++ b/src/ident.rs @@ -24,7 +24,7 @@ const MAX_LABEL_LENGTH: usize = 63; #[derive( Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, SerializeDisplay, DeserializeFromStr, )] -pub struct Name(pub Vec); +pub struct Name(Vec); impl Name { /// Number of labels in the name (e.g., "vm1.test.mesh" has 3 labels). @@ -38,7 +38,7 @@ impl Name { #[must_use] fn first_label(&self) -> &str { // Labels are stored in reverse order, so the first label is last - self.0.last().expect("Name always has at least one label") + self.0.last().assert() } /// Check if this name starts with the given prefix. diff --git a/src/modules/adhoc.rs b/src/modules/adhoc.rs index 473fae3..9b3f800 100644 --- a/src/modules/adhoc.rs +++ b/src/modules/adhoc.rs @@ -501,7 +501,7 @@ pub(crate) fn is_valid_mesh(pattern: &NamePattern) -> bool { return false; }; - base.0.len() == 2 + base.label_count() == 2 } #[cfg(test)] diff --git a/src/trust_engine_bench/node.rs b/src/trust_engine_bench/node.rs index a908128..2680c58 100644 --- a/src/trust_engine_bench/node.rs +++ b/src/trust_engine_bench/node.rs @@ -57,7 +57,8 @@ impl NodeFactory { trusted: bool, ) -> Node { let id = self.allocate_id(); - let name = child_name(parent_namespace, child_label(id, trusted)); + let label = child_label(id, trusted); + let name = child_name(parent_namespace, &label); Node { id, children: Vec::new(), @@ -83,7 +84,8 @@ impl NodeFactory { trusted: bool, ) -> Node { let id = self.allocate_id(); - let mesh_name = child_name(parent_namespace, child_label(id, trusted)); + let label = child_label(id, trusted); + let mesh_name = child_name(parent_namespace, &label); Node { id, children: Vec::new(), @@ -177,9 +179,7 @@ impl Node { } fn root_identity_name(mesh_name: &Name) -> Name { - let mut labels = mesh_name.0.clone(); - labels.push("root".to_string()); - Name(labels) + format!("root.{mesh_name}").parse().assert() } fn child_label(id: NodeId, trusted: bool) -> String { @@ -190,8 +190,6 @@ fn child_label(id: NodeId, trusted: bool) -> String { } } -fn child_name(parent_namespace: &Name, label: String) -> Name { - let mut labels = parent_namespace.0.clone(); - labels.push(label); - Name(labels) +fn child_name(parent_namespace: &Name, label: &str) -> Name { + format!("{label}.{parent_namespace}").parse().assert() }