Make bare node defaults extensible to any type
Refactored the boolean defaults implementation to provide a clean pattern
that users can follow to create custom defaults for any type:

- Moved boolean helpers to  module
- Removed backward compatibility (bool_defaults module)  
- Updated documentation with cleaner examples
- Added comprehensive example showing extension to i32 and String types
- Maintained all existing functionality and tests

Users can now create custom deserializers for any type by implementing
the visitor pattern, while boolean defaults remain convenient to use:

    #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
    enabled: bool,

KDL: enabled          // → true
KDL: enabled #false   // → false (explicit override)
change swxxrusunqoyuzxxvrspvvtuywtstlvw
commit 24748dce7ca74a67136a8bced677cbaf13d09f42
author Alpha Chen <alpha@kejadlen.dev>
date
parent tqymtrxw
diff --git a/examples/boolean_defaults.rs b/examples/boolean_defaults.rs
index ca353d1..a34522d 100644
--- a/examples/boolean_defaults.rs
+++ b/examples/boolean_defaults.rs
@@ -1,22 +1,109 @@
-use serde::Deserialize;
+use serde::de::{self, Visitor};
+use serde::{Deserialize, Deserializer};
+
+// Custom deserializer for integer with default value
+fn retry_count_default<'de, D>(deserializer: D) -> Result<i32, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct RetryCountDefaultVisitor;
+
+    impl<'de> Visitor<'de> for RetryCountDefaultVisitor {
+        type Value = i32;
+
+        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+            write!(formatter, "an integer or bare node name")
+        }
+
+        fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok(value as i32)
+        }
+
+        fn visit_i128<E>(self, value: i128) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok(value as i32)
+        }
+
+        fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok(value as i32)
+        }
+
+        fn visit_unit<E>(self) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok(3) // Default retry count for bare nodes
+        }
+    }
+
+    deserializer.deserialize_any(RetryCountDefaultVisitor)
+}
+
+// Custom deserializer for string with default value
+fn name_default<'de, D>(deserializer: D) -> Result<String, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct NameDefaultVisitor;
+
+    impl<'de> Visitor<'de> for NameDefaultVisitor {
+        type Value = String;
+
+        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+            write!(formatter, "a string or bare node name")
+        }
+
+        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok(value.to_string())
+        }
+
+        fn visit_unit<E>(self) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            Ok("unknown".to_string()) // Default name for bare nodes
+        }
+    }
+
+    deserializer.deserialize_any(NameDefaultVisitor)
+}
 
 #[derive(Debug, Deserialize)]
 struct Config {
-    #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]
+    #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
     enabled: bool,
     
-    #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_false")]
+    #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_false")]
     debug: bool,
     
-    // Regular boolean field (requires explicit value)
-    production: bool,
+    #[serde(deserialize_with = "retry_count_default")]
+    retry_count: i32,
+    
+    #[serde(deserialize_with = "name_default")]
+    username: String,
+    
+    // Regular field (requires explicit value)
+    port: u16,
 }
 
 fn main() {
     let kdl_input = r#"
         enabled          // defaults to true
         debug            // defaults to false  
-        production #true // explicit value required
+        retry_count      // defaults to 3
+        username         // defaults to "unknown"
+        port 8080        // explicit value required
     "#;
     
     let config: Config = serde_kdl2::from_str(kdl_input).unwrap();
@@ -24,24 +111,30 @@ fn main() {
     println!("{:#?}", config);
     // Output:
     // Config {
-    //     enabled: true,     // from bare_true default
-    //     debug: false,      // from bare_false default
-    //     production: true,  // from explicit #true
+    //     enabled: true,           // from bare_true default
+    //     debug: false,            // from bare_false default
+    //     retry_count: 3,          // from custom default
+    //     username: "unknown",     // from custom default
+    //     port: 8080,             // from explicit value
     // }
     
     // You can still override the defaults with explicit values
     let kdl_override = r#"
-        enabled #false   // overrides bare_true default
-        debug #true      // overrides bare_false default
-        production #false
+        enabled #false      // overrides bare_true default
+        debug #true         // overrides bare_false default
+        retry_count 10      // overrides default
+        username "alice"    // overrides default
+        port 3000
     "#;
     
     let config2: Config = serde_kdl2::from_str(kdl_override).unwrap();
     println!("{:#?}", config2);
     // Output:
     // Config {
-    //     enabled: false,    // explicit override
-    //     debug: true,       // explicit override
-    //     production: false,
+    //     enabled: false,     // explicit override
+    //     debug: true,        // explicit override
+    //     retry_count: 10,    // explicit override
+    //     username: "alice",  // explicit override
+    //     port: 3000,
     // }
 }
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index b92eb56..ee8bbfe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -49,31 +49,34 @@
 //! enabled #true
 //! ```
 //!
-//! ### Boolean Shorthand
+//! ### Bare Node Defaults
 //!
-//! Boolean fields can use custom defaults for bare node names (without arguments)
-//! using the `deserialize_with` attribute:
+//! You can specify custom defaults for bare node names (nodes without arguments)
+//! using the provided helper functions:
 //!
 //! ```rust
 //! use serde::Deserialize;
 //!
 //! #[derive(Deserialize)]
 //! struct Config {
-//!     #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]
+//!     #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
 //!     enabled: bool,
 //!     
-//!     #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_false")]
-//!     disabled: bool,
+//!     #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_false")]
+//!     debug: bool,
 //! }
 //! ```
 //!
 //! ```kdl
-//! enabled          // → true (due to bare_true)
-//! disabled         // → false (due to bare_false)
-//! enabled #false   // → false (explicit value overrides default)
-//! disabled #true   // → true (explicit value overrides default)
+//! enabled          // → true
+//! debug            // → false
+//! enabled #false   // → false (explicit override)
+//! debug #true      // → true (explicit override)
 //! ```
 //!
+//! For other types, you can create custom deserializer functions following
+//! the same visitor pattern (see examples for details).
+//!
 //! ### Nested Structs
 //!
 //! Nested structs use children blocks:
@@ -147,74 +150,89 @@ pub use de::{from_doc, from_str};
 pub use error::Error;
 pub use ser::{to_doc, to_string, to_string_pretty};
 
-/// Serde helper functions for custom boolean defaults with bare node names.
-pub mod bool_defaults {
+/// Serde helpers for custom defaults with bare node names.
+pub mod bare_defaults {
     use serde::{de, Deserializer};
 
-    /// Deserializes a boolean field where bare node names default to `true`.
-    /// 
-    /// Use with `#[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]`.
-    /// 
-    /// # Examples
-    /// 
-    /// ```kdl
-    /// enabled        // → true
-    /// enabled #true  // → true
-    /// enabled #false // → false
-    /// ```
-    pub fn bare_true<'de, D>(deserializer: D) -> Result<bool, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        bare_default::<D, true>(deserializer)
-    }
-
-    /// Deserializes a boolean field where bare node names default to `false`.
-    /// 
-    /// Use with `#[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_false")]`.
-    /// 
-    /// # Examples
-    /// 
-    /// ```kdl
-    /// disabled        // → false
-    /// disabled #true  // → true
-    /// disabled #false // → false
-    /// ```
-    pub fn bare_false<'de, D>(deserializer: D) -> Result<bool, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        bare_default::<D, false>(deserializer)
-    }
+    /// Boolean-specific bare default deserializers.
+    pub mod bool {
+        use super::*;
 
-    fn bare_default<'de, D, const DEFAULT: bool>(deserializer: D) -> Result<bool, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        struct BareDefaultVisitor<const DEFAULT: bool>;
+        /// Deserializes a boolean field where bare node names default to `true`.
+        /// 
+        /// Use with `#[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]`.
+        /// 
+        /// # Examples
+        /// 
+        /// ```kdl
+        /// enabled        // → true
+        /// enabled #true  // → true
+        /// enabled #false // → false
+        /// ```
+        pub fn bare_true<'de, D>(deserializer: D) -> Result<bool, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            deserialize_bool_with_bare_default(deserializer, true)
+        }
 
-        impl<'de, const DEFAULT: bool> de::Visitor<'de> for BareDefaultVisitor<DEFAULT> {
-            type Value = bool;
+        /// Deserializes a boolean field where bare node names default to `false`.
+        /// 
+        /// Use with `#[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_false")]`.
+        /// 
+        /// # Examples
+        /// 
+        /// ```kdl
+        /// disabled        // → false
+        /// disabled #true  // → true
+        /// disabled #false // → false
+        /// ```
+        pub fn bare_false<'de, D>(deserializer: D) -> Result<bool, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            deserialize_bool_with_bare_default(deserializer, false)
+        }
 
-            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
-                write!(formatter, "a boolean value or bare node name")
+        fn deserialize_bool_with_bare_default<'de, D>(
+            deserializer: D,
+            default_value: bool,
+        ) -> Result<bool, D::Error>
+        where
+            D: Deserializer<'de>,
+        {
+            struct BareDefaultBoolVisitor {
+                default_value: bool,
             }
 
-            fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
-            where
-                E: de::Error,
-            {
-                Ok(value)
-            }
+            impl<'de> de::Visitor<'de> for BareDefaultBoolVisitor {
+                type Value = bool;
 
-            fn visit_unit<E>(self) -> Result<Self::Value, E>
-            where
-                E: de::Error,
-            {
-                Ok(DEFAULT)
+                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                    write!(formatter, "a boolean value or bare node name")
+                }
+
+                fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
+                where
+                    E: de::Error,
+                {
+                    Ok(value)
+                }
+
+                fn visit_unit<E>(self) -> Result<Self::Value, E>
+                where
+                    E: de::Error,
+                {
+                    Ok(self.default_value)
+                }
             }
-        }
 
-        deserializer.deserialize_any(BareDefaultVisitor::<DEFAULT>)
+            deserializer.deserialize_any(BareDefaultBoolVisitor { default_value })
+        }
     }
+
+    // For other types, users can create custom deserializer functions following
+    // the pattern demonstrated in the bool module above.
 }
+
+
diff --git a/tests/integration_primitives.rs b/tests/integration_primitives.rs
index 434faaa..5a1f703 100644
--- a/tests/integration_primitives.rs
+++ b/tests/integration_primitives.rs
@@ -121,10 +121,10 @@ roundtrip!(
 
 #[derive(Debug, Deserialize, PartialEq)]
 struct BooleanDefaults {
-    #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]
+    #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
     enabled: bool,
     
-    #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_false")]
+    #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_false")]
     disabled: bool,
 }
 
@@ -175,7 +175,7 @@ deser_ok!(
 fn boolean_defaults_single_bare_true() {
     #[derive(Debug, Deserialize, PartialEq)]
     struct W {
-        #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]
+        #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
         flag: bool,
     }
     let val: W = serde_kdl2::from_str("flag").unwrap();
@@ -187,7 +187,7 @@ fn boolean_defaults_single_bare_true() {
 fn boolean_defaults_single_bare_false() {
     #[derive(Debug, Deserialize, PartialEq)]
     struct W {
-        #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_false")]
+        #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_false")]
         flag: bool,
     }
     let val: W = serde_kdl2::from_str("flag").unwrap();
@@ -199,7 +199,7 @@ fn boolean_defaults_single_bare_false() {
 fn boolean_defaults_explicit_false_with_bare_true() {
     #[derive(Debug, Deserialize, PartialEq)]
     struct W {
-        #[serde(deserialize_with = "serde_kdl2::bool_defaults::bare_true")]
+        #[serde(deserialize_with = "serde_kdl2::bare_defaults::bool::bare_true")]
         flag: bool,
     }
     let val: W = serde_kdl2::from_str("flag #false").unwrap();