tosho_macros/
lib.rs

1#![warn(missing_docs, clippy::empty_docs, rustdoc::broken_intra_doc_links)]
2#![doc = include_str!("../README.md")]
3
4use proc_macro::TokenStream;
5
6mod common;
7mod deser;
8mod enums;
9mod structs;
10
11/// Derives [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) for an enum using [`std::fmt::Display`]
12///
13/// # Example
14/// ```rust
15/// # use tosho_macros::SerializeEnum;
16/// #
17/// #[derive(SerializeEnum)]
18/// enum TestEnum {
19///     Create,
20///     Read,
21/// }
22///
23/// impl std::fmt::Display for TestEnum {
24///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25///         match self {
26///             TestEnum::Create => write!(f, "create"),
27///             TestEnum::Read => write!(f, "read"),
28///         }
29///     }
30/// }
31///
32/// let test_enum = TestEnum::Create;
33/// assert_eq!(test_enum.to_string(), "create");
34/// ```
35#[proc_macro_derive(SerializeEnum)]
36pub fn serializenum_derive(input: TokenStream) -> TokenStream {
37    let ast = syn::parse(input).unwrap();
38    deser::impl_serenum_derive(&ast)
39}
40
41/// Derives [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) for an enum using [`std::str::FromStr`]
42///
43/// # Example
44/// ```rust
45/// # use tosho_macros::DeserializeEnum;
46/// #
47/// #[derive(DeserializeEnum, PartialEq, Eq, Debug)]
48/// enum TestEnum {
49///     Create,
50///     Read,
51/// }
52///
53/// tosho_macros::enum_error!(TestEnumFromStrError);
54///
55/// impl std::str::FromStr for TestEnum {
56///     type Err = TestEnumFromStrError;
57///     
58///     fn from_str(s: &str) -> Result<Self, Self::Err> {
59///          match s {
60///             "create" => Ok(TestEnum::Create),
61///             "read" => Ok(TestEnum::Read),
62///             _ => Err(TestEnumFromStrError {
63///                 original: s.to_string(),
64///             }),
65///         }
66///     }
67/// }
68///
69/// let test_enum: TestEnum = "create".parse().unwrap();
70/// assert_eq!(test_enum, TestEnum::Create);
71/// ```
72#[proc_macro_derive(DeserializeEnum)]
73pub fn deserializeenum_derive(input: TokenStream) -> TokenStream {
74    let ast = syn::parse(input).unwrap();
75    deser::impl_deserenum_derive(&ast)
76}
77
78/// Derives [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) for an enum in i32 mode.
79///
80/// # Example
81/// ```rust
82/// # use tosho_macros::SerializeEnum32;
83/// #
84/// #[derive(SerializeEnum32)]
85/// enum TestEnum {
86///     Create = 0,
87///     Read = 1,
88/// }
89/// ```
90#[proc_macro_derive(SerializeEnum32)]
91pub fn serializenum32_derive(input: TokenStream) -> TokenStream {
92    let ast = syn::parse(input).unwrap();
93    deser::impl_serenum32_derive(&ast)
94}
95
96/// Derives [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) for an enum in i32 mode.
97///
98/// # Example
99/// ```rust
100/// # use tosho_macros::DeserializeEnum32;
101/// #
102/// #[derive(DeserializeEnum32)]
103/// enum TestEnum {
104///     Create = 0,
105///     Read = 1,
106/// }
107/// ```
108#[proc_macro_derive(DeserializeEnum32)]
109pub fn deserializeenum32_derive(input: TokenStream) -> TokenStream {
110    let ast = syn::parse(input).unwrap();
111    deser::impl_deserenum32_derive(&ast, false)
112}
113
114/// Derives [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) for an enum in i32 mode with fallback to [`std::default::Default`].
115///
116/// # Example
117/// ```rust
118/// # use tosho_macros::DeserializeEnum32Fallback;
119/// #
120/// #[derive(DeserializeEnum32Fallback, Default)]
121/// enum TestEnum {
122///     #[default]
123///     Unknown = -1,
124///     Create = 0,
125///     Read = 1,
126/// }
127/// ```
128#[proc_macro_derive(DeserializeEnum32Fallback)]
129pub fn deserializeenum32fallback_derive(input: TokenStream) -> TokenStream {
130    let ast = syn::parse(input).unwrap();
131    deser::impl_deserenum32_derive(&ast, true)
132}
133
134/// Derives an enum that would implement `.to_name()`
135///
136/// # Example
137/// ```rust
138/// # use tosho_macros::EnumName;
139/// #
140/// #[derive(EnumName, Clone, Debug)]
141/// enum TestEnum {
142///     Create,
143///     Read,
144/// }
145///
146/// let test_enum = TestEnum::Create;
147/// assert_eq!(test_enum.to_name(), "Create");
148/// ```
149#[proc_macro_derive(EnumName)]
150pub fn enumname_derive(input: TokenStream) -> TokenStream {
151    let ast = syn::parse(input).unwrap();
152    enums::impl_enumname_derive(&ast)
153}
154
155/// Derives an enum that would implement `::count()` to return the number of variants
156///
157/// # Example
158/// ```rust
159/// # use tosho_macros::EnumCount;
160/// #
161/// #[derive(EnumCount, Clone, Debug)]
162/// enum TestEnum {
163///     Create,
164///     Read,
165/// }
166///
167/// assert_eq!(TestEnum::count(), 2);
168/// ```
169#[proc_macro_derive(EnumCount)]
170pub fn enumcount_derive(input: TokenStream) -> TokenStream {
171    let ast = syn::parse(input).unwrap();
172    enums::impl_enumcount_derive(&ast)
173}
174
175/// Derives an enum that would implement [`From<u32>`].
176#[proc_macro_derive(EnumU32)]
177pub fn enumu32_derive(input: TokenStream) -> TokenStream {
178    let ast = syn::parse(input).unwrap();
179    enums::impl_enumu32_derive(&ast, false)
180}
181
182/// Derives an enum that would implement [`From<u32>`] with fallback.
183#[proc_macro_derive(EnumU32Fallback)]
184pub fn enumu32fallback_derive(input: TokenStream) -> TokenStream {
185    let ast = syn::parse(input).unwrap();
186    enums::impl_enumu32_derive(&ast, true)
187}
188
189/// Create an error struct for an enum that implements [`std::fmt::Display`] that can be used
190/// when using other macros to derive [`std::str::FromStr`] for an enum.
191///
192/// # Example
193/// ```rust
194/// # use tosho_macros::enum_error;
195/// #
196/// enum TestEnum {
197///     Foo,
198///     Bar,
199/// }
200///
201/// enum_error!(TestEnumFromStrError);
202/// ```
203#[proc_macro]
204pub fn enum_error(item: TokenStream) -> TokenStream {
205    let input = syn::parse_macro_input!(item as enums::EnumErrorMacroInput);
206    enums::impl_enum_error(&input)
207}
208
209/// Create a decoded base64 string at compile time.
210///
211/// # Example
212/// ```rust
213/// # use tosho_macros::comptime_b64;
214///
215/// let decoded = comptime_b64!("SGVsbG8sIFdvcmxkIQ==");
216/// assert_eq!(decoded, "Hello, World!");
217/// ```
218#[proc_macro]
219pub fn comptime_b64(item: TokenStream) -> TokenStream {
220    let input = syn::parse_macro_input!(item as common::CompTimeBase64Input);
221    common::impl_comptime_b64(&input)
222}
223
224/// Derive the `AutoGetter` macro for a struct
225///
226/// Automatically expand each field into their own getter function for all private field.
227///
228/// # Attributes
229/// The following is an attribute that can be used on each field of the struct.
230/// - `#[copyable]` will make the field return a copy instead of a reference (only for `Copy` types or primitives).
231/// - `#[skip_field]` will make the field not have a getter or be skipped.
232/// - `#[deref_clone]` will not return the reference and will return an "Owned" type (no actual cloning happens).
233///
234/// And, an attribute can be used on the struct as well.
235/// - `#[auto_getters(unref = true)]` similar to `#[copyable]` but for all the fields.
236/// - `#[auto_getters(cloned = true)]` similar to `#[deref_clone]` but for all the fields.
237///
238/// # Examples
239/// ```rust
240/// # use tosho_macros::AutoGetter;
241/// #
242/// #[derive(AutoGetter)]
243/// pub struct Data {
244///     #[copyable]
245///     id: i64,
246///     username: String,
247///     #[skip_field]
248///     pos: u32,
249/// }
250///
251/// # fn main() {
252/// let data = Data { id: 1, username: "test".to_string(), pos: 0 };
253///
254/// assert_eq!(data.id(), 1);
255/// assert_eq!(data.username(), "test");
256/// // "pos" field doesn't have getter
257/// assert_eq!(data.pos, 0);
258/// # }
259/// ```
260///
261/// Another one with `cloned` or `deref_clone`
262/// ```rust,no_run
263/// # use tosho_macros::AutoGetter;
264/// #
265/// #[derive(AutoGetter)]
266/// pub struct Data {
267///     #[copyable]
268///     id: i64,
269///     #[deref_clone]
270///     username: String,
271/// }
272///
273/// # fn main() {
274/// let data = Data { id: 1, username: "test".to_string() };
275/// let owned_username = data.username(); // Data is now "moved" or "owned"
276///
277/// assert_eq!(owned_username, "test");
278/// # }
279/// ```
280#[proc_macro_derive(
281    AutoGetter,
282    attributes(auto_getters, copyable, skip_field, deref_clone)
283)]
284pub fn autogetter_derive(input: TokenStream) -> TokenStream {
285    // Parse the input tokens into a syntax tree
286    let input = syn::parse_macro_input!(input as syn::DeriveInput);
287
288    // Generate the implementation of the trait
289    structs::impl_autogetter(&input)
290}
291
292/// Create a function that is derived from the enum and returns the
293/// documentation of the variant as a static string that can be used.
294///
295/// # Example
296/// ```rust
297/// # use tosho_macros::AutoDocFields;
298/// #
299/// #[derive(AutoDocFields)]
300/// enum TestEnum {
301///     /// Create a new item
302///     Create,
303///     /// Read an item
304///     Read,
305///     NoComment,
306/// }
307///
308/// # fn main() {
309/// assert_eq!(TestEnum::Create.get_doc(), Some("Create a new item"));
310/// assert_eq!(TestEnum::Read.get_doc(), Some("Read an item"));
311/// assert_eq!(TestEnum::NoComment.get_doc(), None);
312/// # }
313/// ```
314#[proc_macro_derive(AutoDocFields)]
315pub fn autodocfields_derive(input: TokenStream) -> TokenStream {
316    let ast = syn::parse(input).unwrap();
317    enums::impl_auto_doc_fiels(&ast)
318}
319
320/// A custom derive macro that follows similar pattern to `prost::Enumeration`
321///
322/// This version allows us to define an enum with an unrecognized variant
323/// instead of falling back to a default value.
324///
325/// # Example
326/// ```rust
327/// # use tosho_macros::ProstEnumUnrecognized;
328/// #
329/// #[derive(ProstEnumUnrecognized, Debug, PartialEq, Eq, PartialOrd, Ord)]
330/// enum TestEnum {
331///     Any = 0,
332///     Paid = 1,
333///     // #[invalid_enum] is always required to be present
334///     #[invalid_enum]
335///     Unrecognized = -1,
336/// }
337///
338/// # fn main() {
339/// assert_eq!(TestEnum::try_from(2).unwrap(), TestEnum::Unrecognized);
340/// assert_eq!(TestEnum::try_from(0).unwrap(), TestEnum::Any);
341/// # }
342/// ```
343#[proc_macro_derive(ProstEnumUnrecognized, attributes(invalid_enum))]
344pub fn prost_enum_unrecognized_derive(input: TokenStream) -> TokenStream {
345    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
346    enums::impl_prost_enum_unrecognized(&ast)
347}