lib.rsannotatedlib.rssource363 lines · 11.8 KB · raw
1mod config;
2mod encode;
3mod enums;
4mod partialclone;
5mod serialization;
6
7use proc_macro::TokenStream;
8
9/// Implement the `std::fmt::Display` trait for the given enum. Only supports enums which have only
10/// fieldless variants.
11///
12/// This macro also implements a method `to_str(&self) -> &'static str` for cases where an owned
13/// String is not needed.
14///
15/// # Panics
16///
17/// This macro will panic if applied to a struct, a union, or an enum with any variants that have
18/// fields.
19#[proc_macro_derive(EnumDisplay)]
20pub fn enum_display(input: TokenStream) -> TokenStream {
21    enums::enum_display(input)
22}
23
24/// Implement the `std::str::FromStr` trait for the given enum, with `FromStr::Err` set to `String`.
25/// Only supports enums which have only fieldless variants. The generated implementation will be
26/// case-insensitive.
27///
28/// # Panics
29///
30/// This macro will panic if applied to a struct, a union, or an enum with any variants that have
31/// fields.
32#[proc_macro_derive(EnumFromStr)]
33pub fn enum_from_str(input: TokenStream) -> TokenStream {
34    enums::enum_from_str(input)
35}
36
37/// On an enum with only fieldless variants, add an `ALL` constant of type `[Self; N]` that contains
38/// every variant of the enum. The variant order in `ALL` will equal the variant declaration order.
39///
40/// Example:
41/// ```
42/// use jgenesis_proc_macros::EnumAll;
43///
44/// #[derive(Debug, PartialEq, EnumAll)]
45/// enum Foo {
46///     A,
47///     B,
48///     C,
49/// }
50///
51/// // Explicit type for clarity
52/// let expected: [Foo; 3] = [Foo::A, Foo::B, Foo::C];
53/// assert_eq!(Foo::ALL, expected);
54/// ```
55///
56/// # Panics
57///
58/// This macro will panic if applied to a struct, a union, or an enum with non-fieldless variants.
59#[proc_macro_derive(EnumAll)]
60pub fn enum_all(input: TokenStream) -> TokenStream {
61    enums::enum_all(input)
62}
63
64/// Implement the `clap::ValueEnum` trait for a struct, using a custom implementation rather than
65/// the one provided by `derive(clap::ValueEnum)`.
66///
67/// The implementation differs only in the string values generated. Where `derive(clap::ValueEnum)`
68/// lowercases variant names and inserts a `-` character at every word break, this implementation
69/// uses the variant name directly.
70///
71/// This macro requires that the [`EnumAll`] and [`EnumDisplay`] macros are also used.
72#[proc_macro_derive(CustomValueEnum, attributes(value_enum))]
73pub fn custom_value_enum(input: TokenStream) -> TokenStream {
74    enums::custom_value_enum(input)
75}
76
77/// Implement the `std::fmt::Display` trait for a struct, with an implementation meant for
78/// pretty-printing configs. By default all fields are printed using their own `std::fmt::Display`
79/// implementation.
80///
81/// For example:
82/// ```
83/// use jgenesis_proc_macros::ConfigDisplay;
84///
85/// #[derive(ConfigDisplay)]
86/// struct Config {
87///     foo: u32,
88///     bar: String,
89/// }
90///
91/// // This prints the following output:
92/// // config:
93/// //   foo: 5
94/// //   bar: asdf
95/// let s = format!("config: {}", Config { foo: 5, bar: "asdf".into() });
96/// assert_eq!(s, "config: \n  foo: 5\n  bar: asdf".to_owned());
97/// ```
98///
99/// The `#[cfg_display(debug_fmt)]` attribute can be used to indicate that a field should be formatted using its
100/// `std::fmt::Debug` implementation rather than its `std::fmt::Display` implementation. For example:
101/// ```
102/// use jgenesis_proc_macros::ConfigDisplay;
103///
104/// #[derive(Debug)]
105/// struct NotDisplay(String);
106///
107/// #[derive(ConfigDisplay)]
108/// struct Config {
109///     bar: bool,
110///     #[cfg_display(debug_fmt)]
111///     baz: NotDisplay,
112/// }
113///
114/// // This prints the following output:
115/// // config:
116/// //   foo: Some(5)
117/// //   bar: true
118/// //   baz: NotDisplay("fdsa")
119/// let config = Config {
120///     bar: true,
121///     baz: NotDisplay("fdsa".into()),
122/// };
123/// let s = format!("config: {config}");
124/// assert_eq!(s, "config: \n  bar: true\n  baz: NotDisplay(\"fdsa\")");
125/// ```
126///
127/// The `#[cfg_display(indent_nested)]` attribute allows indenting when printing a field that also implements
128/// the `Display` trait through this macro:
129/// ```
130/// use jgenesis_proc_macros::ConfigDisplay;
131///
132/// #[derive(ConfigDisplay)]
133/// struct Inner {
134///     foo: u32,
135/// }
136///
137/// #[derive(ConfigDisplay)]
138/// struct Outer {
139///     bar: u32,
140///     #[cfg_display(indent_nested)]
141///     inner: Inner,
142///     baz: u32,
143/// }
144///
145/// // This prints the following output:
146/// // config:
147/// //   bar: 3
148/// //   inner:
149/// //     foo: 4
150/// //   baz: 5
151/// let config = Outer {
152///     bar: 3,
153///     inner: Inner { foo: 4 },
154///     baz: 5,
155/// };
156/// let s = format!("config: {config}");
157/// assert_eq!(s, "config: \n  bar: 3\n  inner: \n    foo: 4\n  baz: 5");
158/// ```
159///
160/// Options will automatically be formatted by unwrapping if the value is Some(v) and printing
161/// the string "<None>" if the value is None:
162/// ```
163/// use jgenesis_proc_macros::ConfigDisplay;
164///
165/// #[derive(ConfigDisplay)]
166/// struct Config {
167///     a: Option<String>,
168///     b: Option<u32>,
169/// }
170///
171/// // This prints the following output:
172/// // config:
173/// //   a: hello
174/// //   b: <None>
175/// let config = Config { a: Some("hello".into()), b: None };
176/// let s = format!("config: {config}");
177/// assert_eq!(s, "config: \n  a: hello\n  b: <None>");
178/// ```
179///
180/// # Panics
181///
182/// This macro only supports structs with named fields and it will panic if applied to any other
183/// data type, including structs with no fields.
184#[proc_macro_derive(ConfigDisplay, attributes(cfg_display))]
185pub fn config_display(input: TokenStream) -> TokenStream {
186    config::config_display(input)
187}
188
189/// Implements the `bincode::Encode` trait fpr the given type, with a fake implementation that
190/// does not encode anything and always returns `Ok(())`.
191///
192/// # Panics
193///
194/// This macro will panic only if it is unable to parse its input.
195#[proc_macro_derive(FakeEncode)]
196pub fn fake_encode(input: TokenStream) -> TokenStream {
197    encode::fake_encode(input)
198}
199
200/// Implements the `bincode::Decode` and `bincode::BorrowDecode` traits for the given type,
201/// with fake implementations that do not decode anything and always return `Ok(Self::default())`.
202///
203/// The type must have a `default()` associated function, preferably through implementing the
204/// `Default` trait.
205///
206/// # Panics
207///
208/// This macro will panic only if it is unable to parse its input.
209#[proc_macro_derive(FakeDecode)]
210pub fn fake_decode(input: TokenStream) -> TokenStream {
211    encode::fake_decode(input)
212}
213
214/// Implement the `jgenesis_common::frontend::PartialClone` trait for a given struct or enum.
215///
216/// This macro should be imported through `jgenesis_common` instead of directly from this crate so
217/// that both the macro and the trait are imported.
218///
219/// Fields that are not marked with a `#[partial_clone]` attribute will be cloned using that type's
220/// implementation of the `Clone` trait.
221///
222/// Fields that are marked with `#[partial_clone(default)]` will not be cloned, and instead the
223/// partial clone will contain the default value for that type (via the `Default` trait).
224///
225/// Fields that are marked with `#[partial_clone(partial)]` will be cloned using that type's
226/// implementation of the `PartialClone` trait.
227///
228/// If the struct has any generic type parameters, the `PartialClone` trait will only be implemented
229/// where all of the generic types implement `PartialClone`.
230///
231/// Example:
232/// ```
233/// use jgenesis_common::frontend::PartialClone;
234///
235/// #[derive(Debug, PartialEq, PartialClone)]
236/// struct Nested(Vec<u8>, #[partial_clone(default)] Vec<u16>, String);
237///
238/// #[derive(Debug, PartialEq, PartialClone)]
239/// struct UnnamedFields(Vec<u8>, #[partial_clone(default)] Vec<u16>, #[partial_clone(partial)] Nested);
240///
241/// let inner = Nested(vec![1, 2, 3], vec![4, 5, 6], "hello".into());
242/// let outer = UnnamedFields(vec![7, 8, 9], vec![10, 11, 12], inner);
243///
244/// let expected = UnnamedFields(vec![7, 8, 9], vec![], Nested(vec![1, 2, 3], vec![], "hello".into()));
245/// assert_eq!(outer.partial_clone(), expected);
246/// ```
247///
248/// # Panics
249///
250/// This macro currently only supports structs and enums, and it will panic if applied to a union.
251#[proc_macro_derive(PartialClone, attributes(partial_clone))]
252pub fn partial_clone(input: TokenStream) -> TokenStream {
253    partialclone::partial_clone(input)
254}
255
256/// This macro is fairly specific to the NES Mapper enum, although it could theoretically
257/// be more generalized if needed.
258///
259/// This macro is meant for use with enums in which every variant has exactly one field, and those
260/// fields have different concrete types but extremely similar APIs (e.g. perhaps they all implement
261/// a given trait).
262///
263/// It generates a declarative macro called `match_each_variant` that generates an enum match
264/// expression and always takes three parameters: the value to match on, the identifier to bind the
265/// single unnamed field to, and an expression to use as the match arm for every variant.
266///
267/// Example usage:
268/// ```
269/// use jgenesis_proc_macros::MatchEachVariantMacro;
270///
271/// #[derive(MatchEachVariantMacro)]
272/// enum Example {
273///     VariantA(u16),
274///     VariantB(u32),
275///     VariantC(u64),
276/// }
277///
278/// impl Example {
279///     fn add_20(&self) -> u64 {
280///         match_each_variant!(*self, number => u64::from(number) + 20)
281///     }
282/// }
283///
284/// assert_eq!(25_u64, Example::VariantA(5).add_20());
285/// assert_eq!(30_u64, Example::VariantB(10).add_20());
286/// assert_eq!(35_u64, Example::VariantC(15).add_20());
287/// ```
288///
289/// The macro can optionally wrap the match arm expression in the variant constructor by using the
290/// `:variant` marker:
291/// ```
292/// use jgenesis_proc_macros::MatchEachVariantMacro;
293///
294/// #[derive(Debug, PartialEq, Eq, MatchEachVariantMacro)]
295/// enum Example {
296///     VariantA(u16),
297///     VariantB(u32),
298/// }
299///
300/// impl Example {
301///     fn add_20(&self) -> Self {
302///         match_each_variant!(*self, number => :variant(number + 20))
303///     }
304/// }
305///
306/// assert_eq!(Example::VariantA(25), Example::VariantA(5).add_20());
307/// assert_eq!(Example::VariantB(120), Example::VariantB(100).add_20());
308/// ```
309///
310/// # Panics
311///
312/// This macro will panic if applied to a struct, a union, or an enum in which not every variant
313/// has exactly one unnamed field.
314#[proc_macro_derive(MatchEachVariantMacro)]
315pub fn match_each_variant_macro(input: TokenStream) -> TokenStream {
316    enums::match_each_variant_macro(input)
317}
318
319/// Augment a struct definition such that when deserializing a struct value using serde, if any
320/// errors are encountered while deserializing a field, the deserializer will set that field based
321/// on the struct's [`Default::default()`] implementation instead of propagating the error.
322///
323/// This macro can only be applied to struct definitions, not enums or unions. It also only supports
324/// structs with named fields, not tuple structs. The struct cannot have any generic parameters.
325///
326/// The struct must implement the [`Default`] trait and it must use the [`serde::Deserialize`] derive
327/// macro.
328///
329/// The macro adds a `#[serde(deserialize_with = ...)]` attribute to each field, so no fields can
330/// already have that attribute or else the generated code will not compile.
331///
332/// At runtime, all deserialization errors encountered will be logged at ERROR level.
333///
334/// Example usage:
335/// ```
336/// use jgenesis_proc_macros::deserialize_default_on_error;
337///
338/// #[deserialize_default_on_error]
339/// #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
340/// #[serde(default)]
341/// struct Foo {
342///     a: u32,
343///     b: String,
344/// }
345///
346/// impl Default for Foo {
347///     fn default() -> Self {
348///         Self { a: 5, b: "hello".into() }
349///     }
350/// }
351///
352/// const TOML_INVALID_A: &str = r#"
353/// a = "not a number"
354/// b = "world"
355/// "#;
356///
357/// let foo: Foo = toml::from_str(TOML_INVALID_A).unwrap();
358/// assert_eq!(foo, Foo { a: 5, b: "world".into() });
359/// ```
360#[proc_macro_attribute]
361pub fn deserialize_default_on_error(_attrs: TokenStream, input: TokenStream) -> TokenStream {
362    serialization::deserialize_default_on_error(input)
363}