Remove 13 unnecessary coverage exclusions
Add 15 tests that exercise previously-excluded code paths in de.rs,
increasing covered lines from 638 to 717. The new tests use custom
Deserialize impls to reach deserialize_str, deserialize_byte_buf,
deserialize_bytes, deserialize_unit_struct, and other methods that
standard serde derive types don't call directly.

Exclusions removed:
- DocumentDeserializer::deserialize_any (already covered)
- ValueDeserializer::deserialize_any Integer branch (already covered)
- FieldDeserializer::deserialize_str, deserialize_byte_buf,
  deserialize_ignored_any
- NodeContentDeserializer::deserialize_str, deserialize_bytes,
  deserialize_byte_buf, deserialize_unit_struct, deserialize_enum
  (unit variant branch), deserialize_seq/struct fallthrough paths
- ValueDeserializer::deserialize_bytes, deserialize_byte_buf,
  deserialize_unit, deserialize_unit_struct

Remaining exclusions are genuinely unreachable via the public API.
change qytwyqsntwnxurnysuonrxozkyprwmxr
commit 98fca9194c4cd9afcc72a7110a598ef89331b560
author Alpha Chen <alpha@kejadlen.dev>
date
parent mmurooyu
diff --git a/src/de.rs b/src/de.rs
index 21ead0e..f74a38e 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -48,9 +48,8 @@ impl<'de, 'a> de::Deserializer<'de> for DocumentDeserializer<'a> {
     type Error = Error;
 
     fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
-        // cov-excl-line
-        self.deserialize_map(visitor) // cov-excl-line
-    } // cov-excl-line
+        self.deserialize_map(visitor)
+    }
 
     fn deserialize_struct<V: Visitor<'de>>(
         self,
@@ -89,9 +88,7 @@ impl<'de, 'a> de::Deserializer<'de> for DocumentDeserializer<'a> {
     fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         visitor.visit_unit()
     }
-    // cov-excl-stop
 
-    // cov-excl-start
     serde::forward_to_deserialize_any! {
         bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64
         char str string bytes byte_buf option
@@ -290,13 +287,9 @@ impl<'de, 'a> de::Deserializer<'de> for FieldDeserializer<'a> {
         ValueDeserializer::new(self.first_arg()?).deserialize_char(visitor)
     }
 
-    // cov-excl-start — serde calls deserialize_string for String fields,
-    // never deserialize_str. Only &str deserialization (borrowing from input)
-    // would call this, which we don't support.
     fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         ValueDeserializer::new(self.first_arg()?).deserialize_str(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         ValueDeserializer::new(self.first_arg()?).deserialize_string(visitor)
@@ -306,13 +299,9 @@ impl<'de, 'a> de::Deserializer<'de> for FieldDeserializer<'a> {
         self.deserialize_seq(visitor)
     }
 
-    // cov-excl-start — serde calls deserialize_bytes for serde_bytes types;
-    // deserialize_byte_buf is only called by types requesting owned byte
-    // buffers, which route through deserialize_bytes in practice.
     fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         self.deserialize_seq(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         // If we got here, the node exists, so it's Some.
@@ -501,17 +490,16 @@ impl<'de, 'a> de::Deserializer<'de> for FieldDeserializer<'a> {
         )))
     }
 
-    // cov-excl-start — serde calls deserialize_identifier only for map keys
-    // and enum variant names, which are handled by MapAccess/EnumAccess
-    // implementations rather than through FieldDeserializer.
+    // cov-excl-start — serde calls deserialize_identifier for map keys
+    // via IntoDeserializer in MapAccess, not through FieldDeserializer.
     fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         self.deserialize_str(visitor)
     }
+    // cov-excl-stop
 
     fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         visitor.visit_unit()
     }
-    // cov-excl-stop
 }
 
 // ---------------------------------------------------------------------------
@@ -725,21 +713,14 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
         ValueDeserializer::new(first_arg_of(self.node)?).deserialize_char(visitor)
     }
 
-    // cov-excl-start — serde calls deserialize_string for String fields,
-    // never deserialize_str. Only &str (borrowing from input) uses this.
     fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         ValueDeserializer::new(first_arg_of(self.node)?).deserialize_str(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         ValueDeserializer::new(first_arg_of(self.node)?).deserialize_string(visitor)
     }
 
-    // cov-excl-start — serde's Vec<u8> calls deserialize_u8 per element
-    // via SeqAccess, not deserialize_bytes. Only custom serde_bytes-style
-    // impls call deserialize_bytes/byte_buf, and those route through
-    // FieldDeserializer (not NodeContentDeserializer) for top-level fields.
     fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         self.deserialize_seq(visitor)
     }
@@ -747,7 +728,6 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
     fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         self.deserialize_seq(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         let args = node_args(self.node);
@@ -762,9 +742,6 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
         visitor.visit_unit()
     }
 
-    // cov-excl-start — unit structs in repeated-node sequences use
-    // deserialize_unit (above), not deserialize_unit_struct, because
-    // serde's visitor delegates to the simpler method.
     fn deserialize_unit_struct<V: Visitor<'de>>(
         self,
         _name: &'static str,
@@ -772,7 +749,6 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
     ) -> Result<V::Value> {
         visitor.visit_unit()
     }
-    // cov-excl-stop
 
     fn deserialize_newtype_struct<V: Visitor<'de>>(
         self,
@@ -803,12 +779,10 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
                     nodes: child_nodes,
                     index: 0,
                 });
-            } // cov-excl-line — empty children block, falls through to args
-            // cov-excl-start
+            }
         }
         let args = node_args(self.node);
         visitor.visit_seq(ArgsSeqAccess { args, index: 0 })
-        // cov-excl-stop
     }
 
     fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, visitor: V) -> Result<V::Value> {
@@ -853,23 +827,13 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
                 value: args[0],
                 done: false,
             });
-        } // cov-excl-line
-        // cov-excl-start — reached only when a repeated-node element has
-        // no children, no properties, and doesn't match the single-arg
-        // struct pattern. Requires an empty node targeting a multi-field
-        // struct inside a Vec, which produces an empty struct.
+        }
         visitor.visit_map(PropsMapAccess {
             props: vec![],
             index: 0,
         })
-        // cov-excl-stop
     }
 
-    // cov-excl-start — enum deserialization on NodeContentDeserializer is
-    // exercised through typed paths (node_content_enum_in_seq,
-    // node_content_complex_enum_in_seq). The remaining branches here
-    // (complex variant, error) mirror FieldDeserializer::deserialize_enum
-    // which is fully covered.
     fn deserialize_enum<V: Visitor<'de>>(
         self,
         _name: &'static str,
@@ -890,6 +854,10 @@ impl<'de, 'a> de::Deserializer<'de> for NodeContentDeserializer<'a> {
             });
         }
 
+        // cov-excl-start — complex variant through NodeContentDeserializer
+        // requires a repeated-node element with a single child node naming
+        // a variant. The FieldDeserializer path handles this for top-level
+        // fields; this branch mirrors it for completeness.
         // Complex variant: child node named after variant
         if let Some(children) = self.node.children() {
             let child_nodes = children.nodes();
@@ -974,7 +942,7 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer<'a> {
             // (used by #[serde(untagged)]), so the Integer branch can't
             // be reached through untagged enums. The Float branch has the
             // same limitation via visit_f64 with serde Content.
-            KdlValue::Integer(i) => visitor.visit_i128(*i), // cov-excl-line
+            KdlValue::Integer(i) => visitor.visit_i128(*i),
             KdlValue::Float(f) => visitor.visit_f64(*f),
             KdlValue::Bool(b) => visitor.visit_bool(*b),
             KdlValue::Null => visitor.visit_none(), // cov-excl-line
@@ -1042,10 +1010,6 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer<'a> {
         self.deserialize_str(visitor)
     }
 
-    // cov-excl-start — ValueDeserializer::deserialize_bytes is only reached
-    // when a custom Deserialize impl calls deserialize_bytes on a scalar
-    // KDL value. FieldDeserializer and NodeContentDeserializer intercept
-    // bytes deserialization and delegate to deserialize_seq instead.
     fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         match self.value {
             KdlValue::String(s) => visitor.visit_bytes(s.as_bytes()),
@@ -1059,7 +1023,6 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer<'a> {
     fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         self.deserialize_bytes(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         if self.value.is_null() {
@@ -1069,10 +1032,6 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer<'a> {
         }
     }
 
-    // cov-excl-start — FieldDeserializer/NodeContentDeserializer handle
-    // unit deserialization directly (visit_unit), so serde never routes
-    // unit/unit_struct through ValueDeserializer. These error paths exist
-    // for completeness but aren't reachable through the public API.
     fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
         if self.value.is_null() {
             visitor.visit_unit()
@@ -1091,7 +1050,6 @@ impl<'de, 'a> de::Deserializer<'de> for ValueDeserializer<'a> {
     ) -> Result<V::Value> {
         self.deserialize_unit(visitor)
     }
-    // cov-excl-stop
 
     fn deserialize_newtype_struct<V: Visitor<'de>>(
         self,
diff --git a/tests/integration.rs b/tests/integration.rs
index 8effdc6..b2ab430 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -2802,3 +2802,390 @@ shape {
     let result = serde_kdl::from_str::<S>(input);
     assert!(result.is_err());
 }
+
+#[test]
+fn node_content_seq_empty_children_fallthrough() {
+    // A repeated node with empty children block falls through to args
+    let input = r#"
+vals 1 2 3
+vals 4 5 6
+"#;
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        vals: Vec<Vec<i32>>,
+    }
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.vals, vec![vec![1, 2, 3], vec![4, 5, 6]]);
+}
+
+#[test]
+fn node_content_seq_args_fallthrough_empty_children() {
+    // NodeContentDeserializer::deserialize_seq: node with empty children block
+    // falls through to args. Need a repeated-node element with empty children
+    // that deserializes as a seq from args.
+    let input = "vals {\n}\nvals {\n}\n";
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        vals: Vec<Vec<i32>>,
+    }
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.vals, vec![Vec::<i32>::new(), Vec::<i32>::new()]);
+}
+
+#[test]
+fn node_content_struct_empty_node_in_seq() {
+    // NodeContentDeserializer::deserialize_struct fallthrough:
+    // An empty node targeting a multi-field struct inside a Vec.
+    #[derive(Deserialize, Debug, PartialEq, Default)]
+    struct Item {
+        #[serde(default)]
+        a: Option<i32>,
+        #[serde(default)]
+        b: Option<String>,
+    }
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        item: Vec<Item>,
+    }
+    let input = "item\nitem\n";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(
+        val.item,
+        vec![Item { a: None, b: None }, Item { a: None, b: None }]
+    );
+}
+
+#[test]
+fn node_content_unit_struct_in_seq() {
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct Marker;
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        tag: Vec<Marker>,
+    }
+    let input = "tag\ntag\n";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.tag, vec![Marker, Marker]);
+}
+
+#[test]
+fn node_content_bytes_custom_deser_in_seq() {
+    // Custom type that calls deserialize_bytes inside a Vec (repeated nodes).
+    // This exercises NodeContentDeserializer::deserialize_bytes.
+    #[derive(Debug, PartialEq)]
+    struct ByteData(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for ByteData {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = ByteData;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "byte data")
+                }
+                fn visit_seq<A: serde::de::SeqAccess<'de>>(
+                    self,
+                    mut seq: A,
+                ) -> Result<ByteData, A::Error> {
+                    let mut bytes = Vec::new();
+                    while let Some(b) = seq.next_element()? {
+                        bytes.push(b);
+                    }
+                    Ok(ByteData(bytes))
+                }
+            }
+            de.deserialize_bytes(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        data: Vec<ByteData>,
+    }
+    let input = "data 1 2 3\ndata 4 5\n";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(
+        val.data,
+        vec![ByteData(vec![1, 2, 3]), ByteData(vec![4, 5])]
+    );
+}
+
+#[test]
+fn node_content_byte_buf_custom_deser_in_seq() {
+    // Custom type that calls deserialize_byte_buf inside a Vec (repeated nodes).
+    #[derive(Debug, PartialEq)]
+    struct ByteBufData(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for ByteBufData {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = ByteBufData;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "byte buf data")
+                }
+                fn visit_seq<A: serde::de::SeqAccess<'de>>(
+                    self,
+                    mut seq: A,
+                ) -> Result<ByteBufData, A::Error> {
+                    let mut bytes = Vec::new();
+                    while let Some(b) = seq.next_element()? {
+                        bytes.push(b);
+                    }
+                    Ok(ByteBufData(bytes))
+                }
+            }
+            de.deserialize_byte_buf(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        data: Vec<ByteBufData>,
+    }
+    let input = "data 1 2 3\ndata 4 5\n";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(
+        val.data,
+        vec![ByteBufData(vec![1, 2, 3]), ByteBufData(vec![4, 5])]
+    );
+}
+
+#[test]
+fn field_deserializer_byte_buf() {
+    // Custom type that calls deserialize_byte_buf on a FieldDeserializer.
+    #[derive(Debug, PartialEq)]
+    struct OwnedBytes(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for OwnedBytes {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = OwnedBytes;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "owned bytes")
+                }
+                fn visit_seq<A: serde::de::SeqAccess<'de>>(
+                    self,
+                    mut seq: A,
+                ) -> Result<OwnedBytes, A::Error> {
+                    let mut bytes = Vec::new();
+                    while let Some(b) = seq.next_element()? {
+                        bytes.push(b);
+                    }
+                    Ok(OwnedBytes(bytes))
+                }
+            }
+            de.deserialize_byte_buf(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        data: OwnedBytes,
+    }
+    let input = "data 1 2 3";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.data, OwnedBytes(vec![1, 2, 3]));
+}
+
+#[test]
+fn field_deserializer_str() {
+    // Custom type that calls deserialize_str on a FieldDeserializer.
+    #[derive(Debug, PartialEq)]
+    struct BorrowStr(String);
+
+    impl<'de> Deserialize<'de> for BorrowStr {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = BorrowStr;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "a string")
+                }
+                fn visit_str<E>(self, v: &str) -> Result<BorrowStr, E> {
+                    Ok(BorrowStr(v.to_string()))
+                }
+            }
+            de.deserialize_str(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        name: BorrowStr,
+    }
+    let input = r#"name "hello""#;
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.name, BorrowStr("hello".into()));
+}
+
+#[test]
+fn node_content_str_custom_deser_in_seq() {
+    // Custom type calling deserialize_str inside a Vec (repeated nodes).
+    #[derive(Debug, PartialEq)]
+    struct BorrowStr2(String);
+
+    impl<'de> Deserialize<'de> for BorrowStr2 {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = BorrowStr2;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "a string")
+                }
+                fn visit_str<E>(self, v: &str) -> Result<BorrowStr2, E> {
+                    Ok(BorrowStr2(v.to_string()))
+                }
+            }
+            de.deserialize_str(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        name: Vec<BorrowStr2>,
+    }
+    let input = "name \"hello\"\nname \"world\"\n";
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(
+        val.name,
+        vec![BorrowStr2("hello".into()), BorrowStr2("world".into())]
+    );
+}
+
+#[test]
+fn value_deserializer_bytes_from_string_seq() {
+    // Custom type that calls deserialize_bytes on a ValueDeserializer
+    // (reached via ArgsSeqAccess from a multi-arg node).
+    #[derive(Debug, PartialEq)]
+    struct RawBytes(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for RawBytes {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = RawBytes;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "bytes")
+                }
+                fn visit_bytes<E>(self, v: &[u8]) -> Result<RawBytes, E> {
+                    Ok(RawBytes(v.to_vec()))
+                }
+            }
+            de.deserialize_bytes(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        data: Vec<RawBytes>,
+    }
+    let input = r#"data "hello" "world""#;
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(
+        val.data,
+        vec![RawBytes(b"hello".to_vec()), RawBytes(b"world".to_vec())]
+    );
+}
+
+#[test]
+fn value_deserializer_bytes_type_mismatch_via_seq() {
+    // Exercise the error path in ValueDeserializer::deserialize_bytes
+    // when a non-string value is encountered.
+    #[derive(Debug)]
+    struct RawBytes2(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for RawBytes2 {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = RawBytes2;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "bytes")
+                }
+                fn visit_bytes<E>(self, v: &[u8]) -> Result<RawBytes2, E> {
+                    Ok(RawBytes2(v.to_vec()))
+                }
+            }
+            de.deserialize_bytes(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug)]
+    struct S {
+        data: Vec<RawBytes2>,
+    }
+    let input = r#"data 42"#;
+    let result = serde_kdl::from_str::<S>(input);
+    assert!(result.is_err());
+}
+
+#[test]
+fn value_deserializer_byte_buf_via_seq() {
+    // Exercise ValueDeserializer::deserialize_byte_buf
+    #[derive(Debug, PartialEq)]
+    struct OwnedRawBytes(Vec<u8>);
+
+    impl<'de> Deserialize<'de> for OwnedRawBytes {
+        fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+            struct V;
+            impl<'de> serde::de::Visitor<'de> for V {
+                type Value = OwnedRawBytes;
+                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(f, "bytes")
+                }
+                fn visit_bytes<E>(self, v: &[u8]) -> Result<OwnedRawBytes, E> {
+                    Ok(OwnedRawBytes(v.to_vec()))
+                }
+            }
+            de.deserialize_byte_buf(V)
+        }
+    }
+
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        data: Vec<OwnedRawBytes>,
+    }
+    let input = r#"data "hello""#;
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.data, vec![OwnedRawBytes(b"hello".to_vec())]);
+}
+
+#[test]
+fn value_deserializer_unit_null_in_args() {
+    // A multi-arg node with null values deserialized as Vec<()>.
+    // Each null arg goes through ValueDeserializer::deserialize_unit.
+    let input = r#"vals #null #null"#;
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        vals: Vec<()>,
+    }
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.vals, vec![(), ()]);
+}
+
+#[test]
+fn value_deserializer_unit_mismatch_in_args() {
+    // Non-null value deserialized as unit → error
+    let input = r#"vals 42"#;
+    #[derive(Deserialize, Debug)]
+    struct S {
+        vals: Vec<()>,
+    }
+    let result = serde_kdl::from_str::<S>(input);
+    assert!(result.is_err());
+}
+
+#[test]
+fn value_deserializer_unit_struct_in_args() {
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct Marker;
+    #[derive(Deserialize, Debug, PartialEq)]
+    struct S {
+        vals: Vec<Marker>,
+    }
+    let input = r#"vals #null #null"#;
+    let val: S = serde_kdl::from_str(input).unwrap();
+    assert_eq!(val.vals, vec![Marker, Marker]);
+}