facet/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![warn(missing_docs)]
3#![warn(clippy::std_instead_of_core)]
4#![warn(clippy::std_instead_of_alloc)]
5#![doc = include_str!("../README.md")]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7#![cfg_attr(docsrs, feature(builtin_syntax))]
8#![cfg_attr(docsrs, feature(prelude_import))]
9#![cfg_attr(docsrs, allow(internal_features))]
10
11pub use facet_core::*;
12
13pub use facet_macros::*;
14
15#[cfg(feature = "reflect")]
16pub use facet_reflect::*;
17
18/// Built-in facet attributes.
19///
20/// These attributes are used with the `#[facet(...)]` syntax without a namespace prefix.
21/// For example: `#[facet(sensitive)]`, `#[facet(rename = "name")]`, `#[facet(skip)]`.
22///
23/// Function-based attributes like `default`, `skip_serializing_if`, and `invariants`
24/// store type-erased function pointers. The `proxy` attribute stores a Shape reference
25/// for custom serialization/deserialization via TryFrom conversions.
26pub mod builtin {
27 // Re-export function pointer types for grammar variants
28 pub use crate::DefaultInPlaceFn;
29 pub use crate::InvariantsFn;
30 pub use crate::SkipSerializingIfFn;
31 pub use crate::TruthyFn;
32 pub use crate::ValidatorFn;
33
34 // Generate built-in attribute grammar.
35 // Uses empty namespace "" for built-in facet attributes.
36 // The `builtin;` flag tells the generator this is inside the facet crate itself,
37 // so definition-time code uses `crate::` instead of `::facet::`.
38 crate::define_attr_grammar! {
39 builtin;
40 ns "";
41 crate_path ::facet::builtin;
42
43 /// Built-in facet attribute types.
44 ///
45 /// These represent the runtime-queryable built-in attributes.
46 /// Attributes with function pointers store the actual function reference.
47 ///
48 /// Attributes annotated with `#[storage(flag)]` are stored in `FieldFlags` for O(1) access.
49 /// Attributes annotated with `#[storage(field)]` are stored in dedicated `Field` struct fields.
50 /// Attributes without `#[storage(...)]` are stored in the `attributes` slice (O(n) lookup).
51 /// <https://facet.rs/guide/attributes/>
52 pub enum Attr {
53 /// Marks a field as containing sensitive data that should be redacted in debug output.
54 ///
55 /// Usage: `#[facet(sensitive)]`
56 #[storage(flag)]
57 Sensitive,
58
59 /// Marks a container as opaque - its inner fields don't need to implement Facet.
60 ///
61 /// Usage: `#[facet(opaque)]`
62 Opaque,
63
64 /// Marks a container as transparent - de/serialization is forwarded to the inner type.
65 /// Used for newtype patterns.
66 ///
67 /// Usage: `#[facet(transparent)]`
68 Transparent,
69
70 /// Marks a field to be flattened into its parent structure.
71 ///
72 /// Usage: `#[facet(flatten)]`
73 #[storage(flag)]
74 Flatten,
75
76 /// Marks a field as a child node (for hierarchical formats like KDL/XML).
77 ///
78 /// Usage: `#[facet(child)]`
79 #[storage(flag)]
80 Child,
81
82 /// Denies unknown fields during deserialization.
83 ///
84 /// Usage: `#[facet(deny_unknown_fields)]`
85 DenyUnknownFields,
86
87 /// Uses the default value when the field is missing during deserialization.
88 /// Stores a function pointer that produces the default value in-place.
89 ///
90 /// Usage: `#[facet(default)]` (uses Default trait) or `#[facet(default = expr)]`
91 ///
92 /// When no explicit value is given (`#[facet(default)]`), the Rust field type's
93 /// `Default::default()` is used. This requires the field type to implement Default.
94 /// For opaque fields, this uses the underlying Rust type's Default, not the
95 /// Facet shape's default.
96 ///
97 /// Note: The HAS_DEFAULT flag is also set when this attribute is present.
98 Default(make_t or $ty::default()),
99
100 /// Skips both serialization and deserialization of this field.
101 ///
102 /// Usage: `#[facet(skip)]`
103 #[storage(flag)]
104 Skip,
105
106 /// Skips serialization of this field.
107 ///
108 /// Usage: `#[facet(skip_serializing)]`
109 #[storage(flag)]
110 SkipSerializing,
111
112 /// Conditionally skips serialization based on a predicate function.
113 /// Stores a type-erased function pointer: `fn(PtrConst) -> bool`.
114 ///
115 /// Usage: `#[facet(skip_serializing_if = is_empty)]`
116 SkipSerializingIf(predicate SkipSerializingIfFn),
117
118 /// Skips serialization unless the value is truthy.
119 /// Uses the type's registered truthiness predicate when available.
120 ///
121 /// Usage: `#[facet(skip_unless_truthy)]`
122 SkipUnlessTruthy,
123
124 /// Skips deserialization of this field (uses default value).
125 ///
126 /// Usage: `#[facet(skip_deserializing)]`
127 #[storage(flag)]
128 SkipDeserializing,
129
130 /// For enums: variants are serialized without a discriminator tag.
131 ///
132 /// Usage: `#[facet(untagged)]`
133 Untagged,
134
135 /// Serializes/Deserializers enum to/from integer based on variant discriminant.
136 ///
137 /// Usage: `#[facet(is_numeric)]`
138 IsNumeric,
139
140 /// Marks a type as Plain Old Data.
141 ///
142 /// POD types have no invariants - any combination of valid field values
143 /// produces a valid instance. This enables safe mutation through reflection
144 /// (poke operations).
145 ///
146 /// Usage: `#[facet(pod)]`
147 Pod,
148
149 /// Renames a field or variant during serialization/deserialization.
150 ///
151 /// Usage: `#[facet(rename = "new_name")]`
152 #[storage(field)]
153 Rename(&'static str),
154
155 /// Renames all fields/variants using a case conversion rule.
156 ///
157 /// Usage: `#[facet(rename_all = "camelCase")]`
158 ///
159 /// Supported rules: camelCase, snake_case, PascalCase, SCREAMING_SNAKE_CASE,
160 /// kebab-case, SCREAMING-KEBAB-CASE
161 RenameAll(&'static str),
162
163 /// Aliases a field or variant during deserialization.
164 ///
165 /// Usage: `#[facet(alias = "additional_name")]`
166 ///
167 /// Allows for deserializing a field from either the alias or the original name.
168 #[storage(field)]
169 Alias(&'static str),
170
171 /// For internally/adjacently tagged enums: the field name for the tag.
172 ///
173 /// Usage: `#[facet(tag = "type")]`
174 Tag(&'static str),
175
176 /// For adjacently tagged enums: the field name for the content.
177 ///
178 /// Usage: `#[facet(content = "data")]`
179 Content(&'static str),
180
181 /// Identifies the type with a tag for self-describing formats.
182 ///
183 /// Usage: `#[facet(type_tag = "com.example.MyType")]`
184 TypeTag(&'static str),
185
186 /// Type invariant validation function.
187 /// Stores a type-erased function pointer: `fn(PtrConst) -> bool`.
188 ///
189 /// Usage: `#[facet(invariants = validate_fn)]`
190 Invariants(predicate InvariantsFn),
191
192 /// Declares the truthiness predicate for this container type.
193 /// Stores a type-erased function pointer: `fn(PtrConst) -> bool`.
194 ///
195 /// Usage: `#[facet(truthy = Self::is_truthy)]`
196 Truthy(predicate TruthyFn),
197
198 /// Applies `skip_unless_truthy` to every field in the container.
199 ///
200 /// Usage: `#[facet(skip_all_unless_truthy)]`
201 SkipAllUnlessTruthy,
202
203 /// Proxy type for serialization and deserialization.
204 /// The proxy type must implement `TryFrom<ProxyType> for FieldType` (for deserialization)
205 /// and `TryFrom<&FieldType> for ProxyType` (for serialization).
206 ///
207 /// Usage: `#[facet(proxy = MyProxyType)]`
208 Proxy(shape_type),
209
210 /// Marks a field as having a recursive type that needs lazy shape resolution.
211 ///
212 /// Use this on fields where the type recursively contains the parent type,
213 /// such as `Vec<Self>`, `Box<Self>`, `Option<Arc<Self>>`, etc.
214 ///
215 /// Without this attribute, such recursive types would cause a compile-time cycle.
216 /// With this attribute, the field's shape is resolved lazily via a closure.
217 ///
218 /// Usage: `#[facet(recursive_type)]`
219 ///
220 /// # Example
221 ///
222 /// ```ignore
223 /// #[derive(Facet)]
224 /// struct Node {
225 /// value: i32,
226 /// #[facet(recursive_type)]
227 /// children: Vec<Node>,
228 /// }
229 /// ```
230 RecursiveType,
231
232 // Note: `traits(...)` and `auto_traits` are compile-time-only directives
233 // processed by the derive macro. They are not stored as runtime attributes.
234 // See DeclaredTraits in facet-macros-impl/src/parsed.rs for their handling.
235 }
236 }
237
238 // Manual Facet impl for Attr since we can't use the derive macro inside the facet crate.
239 // This is a simplified opaque implementation.
240 unsafe impl crate::Facet<'_> for Attr {
241 const SHAPE: &'static crate::Shape = &crate::Shape {
242 id: crate::Shape::id_of::<Self>(),
243 layout: crate::Shape::layout_of::<Self>(),
244 vtable: crate::VTableErased::Direct(&crate::VTableDirect::empty()),
245 type_ops: None,
246 marker_traits: crate::MarkerTraits::empty(),
247 type_identifier: "facet::builtin::Attr",
248 ty: crate::Type::User(crate::UserType::Opaque),
249 def: crate::Def::Undefined,
250 type_params: &[],
251 doc: &[],
252 attributes: &[],
253 type_tag: None,
254 inner: None,
255 builder_shape: None,
256 type_name: None,
257 proxy: None,
258 variance: crate::Variance::COVARIANT,
259 flags: crate::ShapeFlags::empty(),
260 tag: None,
261 content: None,
262 };
263 }
264}
265
266#[cfg(feature = "static_assertions")]
267pub use static_assertions;
268
269/// Define an attribute grammar with type-safe parsing.
270///
271/// This macro generates:
272/// - The attribute types (enum + structs)
273/// - A `__parse_attr!` macro for parsing attribute tokens
274/// - Re-exports for the necessary proc-macros
275///
276/// # Example
277///
278/// ```ignore
279/// facet::define_attr_grammar! {
280/// pub enum Attr {
281/// /// Skip this field entirely
282/// Skip,
283/// /// Rename to a different name
284/// Rename(&'static str),
285/// /// Database column configuration
286/// Column(Column),
287/// }
288///
289/// pub struct Column {
290/// /// Override the database column name
291/// pub name: Option<&'static str>,
292/// /// Mark as primary key
293/// pub primary_key: bool,
294/// }
295/// }
296/// ```
297///
298/// This generates an `Attr` enum and `Column` struct with the specified fields,
299/// along with a `__parse_attr!` macro that can parse attribute syntax like:
300///
301/// - `skip` → `Attr::Skip`
302/// - `rename("users")` → `Attr::Rename("users")`
303/// - `column(name = "user_id", primary_key)` → `Attr::Column(Column { name: Some("user_id"), primary_key: true })`
304///
305/// # Supported Field Types
306///
307/// | Grammar Type | Rust Type | Syntax |
308/// |--------------|-----------|--------|
309/// | `bool` | `bool` | `flag` or `flag = true` |
310/// | `&'static str` | `&'static str` | `name = "value"` |
311/// | `Option<&'static str>` | `Option<&'static str>` | `name = "value"` (optional) |
312/// | `Option<bool>` | `Option<bool>` | `flag = true` (optional) |
313#[macro_export]
314macro_rules! define_attr_grammar {
315 ($($grammar:tt)*) => {
316 $crate::__make_parse_attr! { $($grammar)* }
317 };
318}