serde_saphyr/macros.rs
1//! Public macros for constructing option structs without relying on struct literal syntax.
2//!
3//! These macros exist to keep call sites ergonomic while allowing the crate to evolve
4//! its option structs over time (e.g., adding fields) without forcing breaking changes.
5
6/// Construct [`crate::Options`] from `Default` and a list of field assignments.
7///
8/// Example:
9///
10/// ```rust
11/// # #[cfg(feature = "deserialize")]
12/// # {
13/// use serde_saphyr::options::DuplicateKeyPolicy;
14///
15/// let options = serde_saphyr::options! {
16/// duplicate_keys: DuplicateKeyPolicy::LastWins,
17/// strict_booleans: true,
18/// };
19/// # }
20/// ```
21#[cfg(feature = "deserialize")]
22#[macro_export]
23macro_rules! options {
24 ( $( $tt:tt )* ) => {{
25 let mut opt = $crate::Options::default();
26 $crate::__serde_saphyr_options_apply!(opt, $( $tt )*);
27 opt
28 }};
29}
30
31#[cfg(not(feature = "deserialize"))]
32#[macro_export]
33macro_rules! options {
34 ( $( $tt:tt )* ) => {
35 compile_error!("serde-saphyr `options!` requires feature `deserialize`");
36 };
37}
38
39/// Implementation detail for [`options!`].
40///
41/// This is `#[macro_export]` so that `$crate::...` can resolve it from expansions in
42/// downstream crates.
43#[doc(hidden)]
44#[macro_export]
45macro_rules! __serde_saphyr_options_apply {
46 // End.
47 ($opt:ident,) => {};
48 ($opt:ident) => {};
49
50 // Generic field assignment.
51 ($opt:ident, $field:ident : $value:expr $(, $($rest:tt)*)? ) => {{
52 $opt.$field = $value;
53 $( $crate::__serde_saphyr_options_apply!($opt, $($rest)*); )?
54 }};
55}
56
57/// Construct [`crate::SerializerOptions`] from `Default` and a list of field assignments.
58///
59/// Example:
60///
61/// ```rust
62/// # #[cfg(feature = "serialize")]
63/// # {
64/// let opts = serde_saphyr::ser_options! {
65/// indent_step: 4,
66/// quote_all: true,
67/// };
68/// # }
69/// ```
70#[cfg(feature = "serialize")]
71#[macro_export]
72macro_rules! ser_options {
73 ( $( $tt:tt )* ) => {{
74 let mut opt = $crate::SerializerOptions::default();
75 $crate::__serde_saphyr_serializer_options_apply!(opt, $( $tt )*);
76 opt
77 }};
78}
79
80#[cfg(not(feature = "serialize"))]
81#[macro_export]
82macro_rules! ser_options {
83 ( $( $tt:tt )* ) => {
84 compile_error!("serde-saphyr `ser_options!` requires feature `serialize`");
85 };
86}
87
88/// Construct `Some([``crate::Budget``])` from `Default` and a list of field assignments.
89///
90/// This macro returns `Some(Budget)` (instead of just `Budget`) so it can be embedded
91/// directly inside [`crate::options!`] as the value for `Options::budget`.
92///
93/// Example:
94///
95/// ```rust
96/// # #[cfg(feature = "deserialize")]
97/// # {
98/// let options = serde_saphyr::options! {
99/// budget: serde_saphyr::budget! {
100/// max_nodes: 30,
101/// },
102/// };
103/// # }
104/// ```
105#[cfg(feature = "deserialize")]
106#[macro_export]
107macro_rules! budget {
108 ( $( $field:ident : $value:expr ),* $(,)? ) => {{
109 let mut b = $crate::Budget::default();
110 $(
111 b.$field = $value;
112 )*
113 Some(b)
114 }};
115}
116
117#[cfg(not(feature = "deserialize"))]
118#[macro_export]
119macro_rules! budget {
120 ( $( $field:ident : $value:expr ),* $(,)? ) => {
121 compile_error!("serde-saphyr `budget!` requires feature `deserialize`");
122 };
123}
124
125/// Construct [`crate::options::AliasLimits`] from `Default` and a list of field assignments.
126///
127/// This macro returns `AliasLimits` directly so it can be embedded inside
128/// [`crate::options!`] as the value for `Options::alias_limits`.
129///
130/// Example:
131///
132/// ```rust
133/// # #[cfg(feature = "deserialize")]
134/// # {
135/// let options = serde_saphyr::options! {
136/// alias_limits: serde_saphyr::alias_limits! {
137/// max_replay_stack_depth: 16,
138/// },
139/// };
140/// # let _ = options;
141/// # }
142/// ```
143#[cfg(feature = "deserialize")]
144#[macro_export]
145macro_rules! alias_limits {
146 ( $( $field:ident : $value:expr ),* $(,)? ) => {{
147 let mut limits = $crate::options::AliasLimits::default();
148 $(
149 limits.$field = $value;
150 )*
151 limits
152 }};
153}
154
155#[cfg(not(feature = "deserialize"))]
156#[macro_export]
157macro_rules! alias_limits {
158 ( $( $field:ident : $value:expr ),* $(,)? ) => {
159 compile_error!("serde-saphyr `alias_limits!` requires feature `deserialize`");
160 };
161}
162
163/// Construct [`crate::RenderOptions`] from defaults and a list of field assignments.
164///
165/// This macro exists to keep call sites ergonomic while allowing `RenderOptions` to evolve
166/// over time (e.g., adding fields) without forcing downstream crates to use struct literal
167/// syntax.
168///
169/// Defaults:
170/// - `formatter`: the built-in developer formatter (`DefaultMessageFormatter`)
171/// - `snippets`: [`crate::SnippetMode::Auto`]
172///
173/// Example:
174///
175/// ```rust
176/// # #[cfg(feature = "deserialize")]
177/// # {
178/// use serde_saphyr::{SnippetMode, UserMessageFormatter};
179///
180/// let user = UserMessageFormatter;
181/// let opts = serde_saphyr::render_options! {
182/// formatter: &user,
183/// };
184/// # }
185/// ```
186#[cfg(feature = "deserialize")]
187#[macro_export]
188macro_rules! render_options {
189 ( $( $field:ident : $value:expr ),* $(,)? ) => {{
190 let mut opt = $crate::RenderOptions::default();
191 $(
192 {
193 opt.$field = $value;
194 }
195 )*
196 opt
197 }};
198}
199
200#[cfg(not(feature = "deserialize"))]
201#[macro_export]
202macro_rules! render_options {
203 ( $( $tt:tt )* ) => {
204 compile_error!("serde-saphyr `render_options!` requires feature `deserialize`");
205 };
206}
207
208/// Implementation detail for [`ser_options!`].
209///
210/// This is `#[macro_export]` so that `$crate::...` can resolve it from expansions in
211/// downstream crates.
212#[doc(hidden)]
213#[macro_export]
214macro_rules! __serde_saphyr_serializer_options_apply {
215 // End.
216 ($opt:ident,) => {};
217 ($opt:ident) => {};
218
219 // Special-case indent_step when the value is a literal: enforce at compile time.
220 ($opt:ident, indent_step : $value:literal $(, $($rest:tt)*)? ) => {{
221 const _: () = {
222 // Keep the limit aligned with SerializerOptions' runtime validation.
223 if !($value > 0 && $value <= 64) {
224 panic!("`indent_step` must be in the range 1..=64");
225 }
226 };
227 $opt.indent_step = $value;
228 $( $crate::__serde_saphyr_serializer_options_apply!($opt, $($rest)*); )?
229 }};
230
231 // indent_step for non-const expressions: allow compilation; runtime validation will apply.
232 ($opt:ident, indent_step : $value:expr $(, $($rest:tt)*)? ) => {{
233 $opt.indent_step = $value;
234 $( $crate::__serde_saphyr_serializer_options_apply!($opt, $($rest)*); )?
235 }};
236
237 // Generic field assignment.
238 ($opt:ident, $field:ident : $value:expr $(, $($rest:tt)*)? ) => {{
239 $opt.$field = $value;
240 $( $crate::__serde_saphyr_serializer_options_apply!($opt, $($rest)*); )?
241 }};
242}
243
244// NOTE: `serializer_options!` intentionally removed; `ser_options!` is the canonical macro.