facet_default/
lib.rs

1//! # facet-default
2//!
3//! Derive [`Default`] for your types using facet's plugin system with custom field defaults.
4//!
5//! ## Usage
6//!
7//! ```ignore
8//! use facet::Facet;
9//! use facet_default as default;
10//!
11//! #[derive(Facet, Debug)]
12//! #[facet(derive(Default))]
13//! pub struct Config {
14//!     #[facet(default::value = "localhost")]
15//!     host: String,
16//!     #[facet(default::value = 8080u16)]
17//!     port: u16,
18//!     #[facet(default::func = "default_timeout")]
19//!     timeout: std::time::Duration,
20//!     // No attribute = uses Default::default()
21//!     debug: bool,
22//! }
23//!
24//! fn default_timeout() -> std::time::Duration {
25//!     std::time::Duration::from_secs(30)
26//! }
27//! ```
28//!
29//! ## Attributes
30//!
31//! ### Field Level
32//!
33//! - `#[facet(default::value = literal)]` - Use a literal value (converted via `.into()`)
34//! - `#[facet(default::func = "path")]` - Call a function to get the default value (path as string)
35//!
36//! Fields without attributes use `Default::default()`.
37//!
38//! **Note:** For numeric literals, use type suffixes to ensure correct types (e.g., `8080u16`
39//! instead of `8080` for a `u16` field). String literals are automatically converted via `.into()`.
40//!
41//! ## Enums
42//!
43//! For enums, mark the default variant:
44//!
45//! ```ignore
46//! #[derive(Facet, Debug)]
47//! #[facet(derive(Default))]
48//! #[repr(u8)]
49//! pub enum Status {
50//!     #[facet(default::variant)]
51//!     Pending,
52//!     Active,
53//!     Done,
54//! }
55//! ```
56
57// ============================================================================
58// ATTRIBUTE GRAMMAR
59// ============================================================================
60
61facet::define_attr_grammar! {
62    ns "default";
63    crate_path ::facet_default;
64
65    /// Default attribute types for configuring Default implementation.
66    pub enum Attr {
67        /// Use a literal value for the field default (converted via `.into()`).
68        ///
69        /// Usage: `#[facet(default::value = "hello")]`
70        /// Usage: `#[facet(default::value = 42)]`
71        ///
72        /// Note: The type here is nominally `&'static str` but the plugin template
73        /// uses `@attr_args` which captures the raw tokens, so any value works.
74        Value(&'static str),
75
76        /// Call a function to get the default value.
77        ///
78        /// Usage: `#[facet(default::func = my_default_fn)]`
79        ///
80        /// Note: The type here is nominally `&'static str` but the plugin template
81        /// uses `@attr_args` which captures the raw tokens, so any path works.
82        Func(&'static str),
83
84        /// Mark an enum variant as the default.
85        ///
86        /// Usage: `#[facet(default::variant)]`
87        Variant,
88    }
89}
90
91// ============================================================================
92// PLUGIN TEMPLATE
93// ============================================================================
94
95/// Plugin chain entry point.
96///
97/// Called by `#[derive(Facet)]` when `#[facet(derive(Default))]` is present.
98#[macro_export]
99macro_rules! __facet_invoke {
100    (
101        @tokens { $($tokens:tt)* }
102        @remaining { $($remaining:tt)* }
103        @plugins { $($plugins:tt)* }
104        @facet_crate { $($facet_crate:tt)* }
105    ) => {
106        $crate::__facet_invoke_internal! {
107            @tokens { $($tokens)* }
108            @remaining { $($remaining)* }
109            @plugins {
110                $($plugins)*
111                @plugin {
112                    @name { "Default" }
113                    @template {
114                        impl ::core::default::Default for @Self {
115                            fn default() -> Self {
116                                @if_struct {
117                                    Self {
118                                        @for_field {
119                                            @field_name: @field_default_expr,
120                                        }
121                                    }
122                                }
123                                @if_enum {
124                                    @for_variant {
125                                        @if_attr(default::variant) {
126                                            Self::@variant_name @variant_default_construction
127                                        }
128                                    }
129                                }
130                            }
131                        }
132                    }
133                }
134            }
135            @facet_crate { $($facet_crate)* }
136        }
137    };
138}
139
140/// Internal macro that either chains to next plugin or calls finalize
141#[doc(hidden)]
142#[macro_export]
143macro_rules! __facet_invoke_internal {
144    // No more plugins - call finalize
145    (
146        @tokens { $($tokens:tt)* }
147        @remaining { }
148        @plugins { $($plugins:tt)* }
149        @facet_crate { $($facet_crate:tt)* }
150    ) => {
151        $($facet_crate)*::__facet_finalize! {
152            @tokens { $($tokens)* }
153            @plugins { $($plugins)* }
154            @facet_crate { $($facet_crate)* }
155        }
156    };
157
158    // More plugins - chain to next
159    (
160        @tokens { $($tokens:tt)* }
161        @remaining { $next:path $(, $rest:path)* $(,)? }
162        @plugins { $($plugins:tt)* }
163        @facet_crate { $($facet_crate:tt)* }
164    ) => {
165        $next! {
166            @tokens { $($tokens)* }
167            @remaining { $($rest),* }
168            @plugins { $($plugins)* }
169            @facet_crate { $($facet_crate)* }
170        }
171    };
172}