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