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}