fix: treat single node with children as one sequence element
A single node with non-dash children was deserialized as multiple
sequence elements (one per child). This broke single-element
vec-of-structs, where the node itself should be the sole element
with its children as struct fields. The `-` convention remains
the correct way to express child-based sequences.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/src/de.rs b/src/de.rs
index 6689757..e6d6779 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -353,8 +353,8 @@ impl<'de, 'a> de::Deserializer<'de> for FieldDeserializer<'a> {
// Strategy:
// 1. Multiple nodes with same name → each node is an element
// 2. Single node with `-` children → each `-` child is an element
- // 3. Single node with multiple args → each arg is an element
- // 4. Single node with non-`-` children → children nodes are elements
+ // 3. Single node with children → single-element sequence (the node itself)
+ // 4. Single node with multiple args → each arg is an element
if self.nodes.len() > 1 {
// Multiple nodes → each node contributes one element
@@ -378,17 +378,16 @@ impl<'de, 'a> de::Deserializer<'de> for FieldDeserializer<'a> {
index: 0,
});
}
- // Non-dash children: treat each child as an element
- let child_nodes: Vec<&KdlNode> = children.nodes().iter().collect();
- if !child_nodes.is_empty() {
- return visitor.visit_seq(ChildNodeSeqAccess {
- nodes: child_nodes,
+ // Single node with non-empty children → treat node as single element
+ if !children.nodes().is_empty() {
+ return visitor.visit_seq(MultiNodeSeqAccess {
+ nodes: self.nodes,
index: 0,
});
}
}
- // Arguments as elements
+ // Arguments as elements (or empty seq if no args)
let args = node_args(node);
visitor.visit_seq(ArgsSeqAccess { args, index: 0 })
}
diff --git a/tests/integration.rs b/tests/integration.rs
index 98d0ad1..377cb86 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1493,15 +1493,17 @@ deser_field!(
);
// ── Non-dash children as sequence ──────────────────────────────────────
+// A single node with children is treated as a single-element sequence,
+// not as individual elements. Use `-` convention for child elements.
deser_field!(
deserialize_children_as_sequence,
items: Vec<i32>,
indoc! {"
items {
- item 1
- item 2
- item 3
+ - 1
+ - 2
+ - 3
}
"},
vec![1, 2, 3]
diff --git a/tests/integration_collections.rs b/tests/integration_collections.rs
index 01a1fc0..9c70db2 100644
--- a/tests/integration_collections.rs
+++ b/tests/integration_collections.rs
@@ -129,6 +129,36 @@ roundtrip!(
}
);
+// ── Single-element vec of structs ──────────────────────────────────────
+
+deser_ok!(
+ deserialize_single_element_vec_structs,
+ Cluster,
+ indoc! {r#"
+ server {
+ host "localhost"
+ port 8080
+ }
+ "#},
+ Cluster {
+ server: vec![Server {
+ host: "localhost".into(),
+ port: 8080,
+ }],
+ }
+);
+
+roundtrip!(
+ roundtrip_single_element_vec_structs,
+ Cluster,
+ Cluster {
+ server: vec![Server {
+ host: "localhost".into(),
+ port: 8080,
+ }],
+ }
+);
+
// ── Dash children ──────────────────────────────────────────────────────
deser_field!(