wazabin_jstd_derive/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
4
5enum Backing {
7 Usize,
8 U32,
9}
10
11#[proc_macro_derive(Identifier)]
12pub fn derive_identifier(input: TokenStream) -> TokenStream {
13 let input = parse_macro_input!(input as DeriveInput);
14 let name = input.ident;
15
16 let backing = match input.data {
17 Data::Struct(data) => match data.fields {
18 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
19 match fields.unnamed.first().map(|f| &f.ty) {
20 Some(Type::Path(type_path)) if type_path.path.is_ident("usize") => {
21 Some(Backing::Usize)
22 }
23 Some(Type::Path(type_path)) if type_path.path.is_ident("u32") => {
24 Some(Backing::U32)
25 }
26 _ => None,
27 }
28 }
29 _ => None,
30 },
31 _ => None,
32 };
33
34 let Some(backing) = backing else {
35 return syn::Error::new_spanned(
36 name,
37 "Identifier can only be derived for tuple structs with a single `usize` or `u32` field, e.g. struct MyId(usize);",
38 )
39 .to_compile_error()
40 .into();
41 };
42
43 let (new_body, from_usize_body, into_usize_body, ser_body, de_body) = match backing {
48 Backing::Usize => (
49 quote! { Self(id) },
50 quote! { Self(value) },
51 quote! { id.0 },
52 quote! { self.0 as u64 },
53 quote! { Self(id as usize) },
54 ),
55 Backing::U32 => (
56 quote! { Self(id as u32) },
57 quote! { Self(value as u32) },
58 quote! { id.0 as usize },
59 quote! { self.0 as u64 },
60 quote! { Self(id as u32) },
61 ),
62 };
63
64 TokenStream::from(quote! {
65 impl #name {
66 pub const fn new(id: usize) -> Self {
68 #new_body
69 }
70 }
71
72 impl ::core::marker::Copy for #name {}
73
74 impl ::core::clone::Clone for #name {
75 fn clone(&self) -> Self {
76 *self
77 }
78 }
79
80 impl ::core::default::Default for #name {
81 fn default() -> Self {
82 Self(0)
83 }
84 }
85
86 impl ::core::cmp::PartialEq for #name {
87 fn eq(&self, other: &Self) -> bool {
88 self.0 == other.0
89 }
90 }
91
92 impl ::core::cmp::Eq for #name {}
93
94 impl ::core::cmp::PartialOrd for #name {
95 fn partial_cmp(&self, other: &Self) -> ::core::option::Option<::core::cmp::Ordering> {
96 ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
97 }
98 }
99
100 impl ::core::cmp::Ord for #name {
101 fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
102 ::core::cmp::Ord::cmp(&self.0, &other.0)
103 }
104 }
105
106 impl ::core::hash::Hash for #name {
107 fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
108 ::core::hash::Hash::hash(&self.0, state);
109 }
110 }
111
112 impl ::core::fmt::Debug for #name {
113 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
114 f.debug_tuple(stringify!(#name)).field(&self.0).finish()
115 }
116 }
117
118 impl ::core::fmt::Display for #name {
119 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
120 write!(f, "{}", self.0)
121 }
122 }
123
124 impl From<usize> for #name {
125 fn from(value: usize) -> Self {
126 #from_usize_body
127 }
128 }
129
130 impl From<#name> for usize {
131 fn from(id: #name) -> usize {
132 #into_usize_body
133 }
134 }
135
136
137 impl ::jstd::registry::Identifier for #name {}
138
139 impl ::serde::Serialize for #name {
140 fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error>
141 where
142 S: ::serde::Serializer,
143 {
144 ::serde::Serialize::serialize(&(#ser_body), serializer)
145 }
146 }
147
148 impl<'de> ::serde::Deserialize<'de> for #name {
149 fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
150 where
151 D: ::serde::Deserializer<'de>,
152 {
153 let id = <u64 as ::serde::Deserialize<'de>>::deserialize(deserializer)?;
154 Ok(#de_body)
155 }
156 }
157 })
158}