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}