Skip to content

Commit

Permalink
Correctly handle node paths that start with _ for compose-node-name
Browse files Browse the repository at this point in the history
The compose-node-name feature in Python Reclass treats nodes whose path
in `nodes_path` starts with a `_` as top-level nodes. We extend our
implementation of compose-node-name and the test inventory for
compose-node-name to handle such nodes the same way as Python reclass.
  • Loading branch information
simu committed Jan 27, 2024
1 parent 7290b17 commit e8eed26
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 9 deletions.
35 changes: 26 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct EntityInfo {
loc: PathBuf,
}

#[derive(Eq, PartialEq)]
enum EntityKind {
Node,
Class,
Expand Down Expand Up @@ -159,13 +160,23 @@ fn walk_entity_dir(
// For normal classes, the location is the directory holding the class file.
(cls, relpath.parent().unwrap_or(Path::new("")))
};
let cls = cls
.to_str()
.ok_or(anyhow!(
"Failed to normalize entity {}",
entry.path().display()
))?
.replace(MAIN_SEPARATOR, ".");
let cls = cls.to_str().ok_or(anyhow!(
"Failed to normalize entity {}",
entry.path().display()
))?;
let (cls, loc) = if kind == &EntityKind::Node && max_depth > 1 && cls.starts_with('_') {
// special case node paths starting with _ for compose-node-name
(
cls.split('/').last().ok_or(anyhow!(
"Can't shorten node name for {}",
entry.path().display()
))?,
Path::new(""),
)
} else {
(cls, loc)
};
let cls = cls.replace(MAIN_SEPARATOR, ".");
if let Some(prev) = entity_map.get(&cls) {
return err_duplicate_entity(kind, root, relpath, &cls, &prev.path);
}
Expand Down Expand Up @@ -385,14 +396,20 @@ mod tests {
.unwrap();
c.load_from_file("reclass-config.yml").unwrap();
let r = Reclass::new_from_config(c).unwrap();
assert_eq!(r.nodes.len(), 5);
assert_eq!(r.nodes.len(), 8);
let mut nodes = r.nodes.keys().collect::<Vec<_>>();
nodes.sort();
assert_eq!(nodes, vec!["a", "a.1", "b.1", "c.1", "d"]);
assert_eq!(
nodes,
vec!["a", "a.1", "b.1", "c.1", "c._c.1", "d", "d1", "d2"]
);
assert_eq!(r.nodes["a"].path, PathBuf::from("a.yml"));
assert_eq!(r.nodes["a.1"].path, PathBuf::from("a.1.yml"));
assert_eq!(r.nodes["b.1"].path, PathBuf::from("b/1.yml"));
assert_eq!(r.nodes["c.1"].path, PathBuf::from("c/1.yml"));
assert_eq!(r.nodes["c._c.1"].path, PathBuf::from("c/_c/1.yml"));
assert_eq!(r.nodes["d"].path, PathBuf::from("d.yml"));
assert_eq!(r.nodes["d1"].path, PathBuf::from("_d/d1.yml"));
assert_eq!(r.nodes["d2"].path, PathBuf::from("_d/d/d2.yml"));
}
}
16 changes: 16 additions & 0 deletions src/node/nodeinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ impl NodeInfoMeta {

/// Generates a Mapping suitable to use as meta parameter `_reclass_`
pub(crate) fn as_reclass(&self, config: &Config) -> Result<Mapping> {
let part0 = self
.parts
.iter()
.next()
.ok_or(anyhow!("Can't extract first path segment for node"))?
.to_str()
.ok_or(anyhow!("Unable to convert path segment to a string"))?;
let parts = if config.compose_node_name
&& config
.compatflags
Expand All @@ -61,6 +68,15 @@ impl NodeInfoMeta {
// This matches Python reclass's behavior, but is incorrect for nodes which contain
// literal dots in the file name.
self.name.split('.').collect::<Vec<&str>>()
} else if part0.starts_with('_') {
// Always drop path prefix for paths that start with `_`
vec![self
.parts
.iter()
.last()
.ok_or(anyhow!("Unable to extract last segment from node"))?
.to_str()
.ok_or(anyhow!("Unable to convert path segment to a string"))?]
} else {
// If the compat flag isn't set, we generate the parts list from the provided shortened
// pathbuf containing the path within `nodes_path` which preserves literal dots in the
Expand Down
8 changes: 8 additions & 0 deletions tests/inventory-compose-node-name/nodes/_d/d/d2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# compose-node-name treats nodes whose path in `nodes_path` starts with `_`
# as top-level nodes.
parameters:
node_name: "d2"
short_name: "d2"
path: "d2"
parts: ["d2"]
uri_suffix: "_d/d/d2.yml"
8 changes: 8 additions & 0 deletions tests/inventory-compose-node-name/nodes/_d/d1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# compose-node-name treats nodes whose path in `nodes_path` starts with `_`
# as top-level nodes.
parameters:
node_name: "d1"
short_name: "d1"
path: "d1"
parts: ["d1"]
uri_suffix: "_d/d1.yml"
6 changes: 6 additions & 0 deletions tests/inventory-compose-node-name/nodes/c/_c/1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
node_name: c._c.1
short_name: "1"
path: "c/_c/1"
parts: ["c", "_c", "1"]
uri_suffix: "c/_c/1.yml"

0 comments on commit e8eed26

Please sign in to comment.