1#![doc=include_str!("../README.md")]
2
3mod int_range_ext;
4mod util;
5use util::RangeChecker;
6
7use proc_macro::TokenStream;
8use quote::{quote, ToTokens};
9use syn::{parse_macro_input, spanned::Spanned, Error, ExprRange, ItemEnum, Meta, Type};
10
11#[proc_macro_derive(IntType, attributes(default,))]
12pub fn inttype(input: TokenStream) -> TokenStream {
13 let item = parse_macro_input!(input as ItemEnum);
14
15 let ident = &item.ident;
16 let Some(ty) = item.attrs.iter().find_map(|attr| {
17 let Meta::List(ref meta_list) = attr.meta else {
18 return None;
19 };
20 if !attr.path().is_ident("repr") {
21 return None;
22 }
23
24 syn::parse2::<Type>(meta_list.tokens.clone()).ok()
25 }) else {
26 return Error::new(item.span(), "no #[repr(inttype)] provided.\n`inttype` can be one of `u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, and isize`.\n See https://doc.rust-lang.org/reference/type-layout.html#primitive-representations")
28 .into_compile_error().into();
29 };
30
31 let mut default_var = None;
32 let mut result = None;
33 let var = item
34 .variants
35 .iter()
36 .map(|v| {
37 if v.attrs.iter().any(|attr| attr.path().is_ident("default")) {
38 if default_var.is_some() {
39 result = Some(
40 Error::new(
41 v.span(),
42 "Multiple default variables supplied! should be only one!",
43 )
44 .into_compile_error(),
45 );
46 }
47 default_var = Some(&v.ident);
48 }
49
50 if !matches!(v.fields, syn::Fields::Unit) {
51 result = Some(
52 Error::new(v.span(), "every variant must be Unit kind, like `None`")
53 .into_compile_error(),
54 );
55 }
56 &v.ident
57 })
58 .collect::<Vec<_>>();
59
60 if let Some(ret) = result {
61 return ret.into();
62 }
63
64 let mut token_stream = quote! {
65 impl From<#ident> for #ty {
66 fn from(value: #ident) -> Self {
67 value as Self
68 }
69 }
70 };
71
72 let from = if let Some(default_var) = default_var {
73 quote! {
74 impl From<#ty> for #ident {
75 fn from(value: #ty) -> Self {
76 #![allow(non_upper_case_globals)]
77 #(
78 const #var: #ty = #ident::#var as #ty;
79 )*
80 match value {
81 #( #var => Self::#var, )*
82 _ => Self::#default_var,
83 }
84 }
85 }
86 }
87 } else {
88 quote! {
89 impl TryFrom<#ty> for #ident {
90 type Error = #ty;
91
92 fn try_from(value: #ty) -> Result<Self, Self::Error> {
93 #![allow(non_upper_case_globals)]
94 #(
95 const #var: #ty = #ident::#var as #ty;
96 )*
97 match value {
98 #( #var => Ok(Self::#var), )*
99 _ => Err(value)
100 }
101 }
102 }
103 }
104 };
105
106 token_stream.extend(from);
107 token_stream.into()
108}
109
110#[proc_macro_derive(IntRange, attributes(range,))]
111pub fn new_inttype(input: TokenStream) -> TokenStream {
112 let item = parse_macro_input!(input as ItemEnum);
113
114 let ident = &item.ident;
115 let Some(ty) = item.attrs.iter().find_map(|attr| {
116 let Meta::List(ref meta_list) = attr.meta else {
117 return None;
118 };
119 if !attr.path().is_ident("repr") {
120 return None;
121 }
122
123 syn::parse2::<Type>(meta_list.tokens.clone()).ok()
124 }) else {
125 return Error::new(item.span(), "no #[repr(inttype)] provided.\n`inttype` can be one of `u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, and isize`.\nSee https://doc.rust-lang.org/reference/type-layout.html#primitive-representations")
127 .into_compile_error().into();
128 };
129
130 let ty_str = ty.to_token_stream().to_string();
131
132 let mut checker = RangeChecker::new(ty_str.clone()).unwrap();
133 let mut unit_variants = Vec::with_capacity(item.variants.len());
135 let mut unit_discriminant = Vec::with_capacity(item.variants.len());
136 let mut unnamed_variants = Vec::with_capacity(item.variants.len());
137 let mut unnamed_ranges = Vec::with_capacity(item.variants.len());
138 let mut ranges = Vec::with_capacity(item.variants.len());
139
140 for v in item.variants.iter() {
141 match &v.fields {
142 syn::Fields::Named(_) => {
143 return Error::new(
144 v.fields.span(),
145 "variant can only be Unit/Unamed kind, Examples: A=0,B(u8),",
146 )
147 .into_compile_error()
148 .into()
149 }
150 syn::Fields::Unnamed(fields) => {
152 if fields.unnamed.len() != 1 {
153 return Error::new(
154 fields.span(),
155 format!(
156 "Unnamed variant can only have 1 field, here it must be {}",
157 ty.into_token_stream()
158 ),
159 )
160 .into_compile_error()
161 .into();
162 }
163 if v.discriminant.is_some() {
164 return Error::new(
165 v.discriminant.as_ref().unwrap().1.span(),
166 "Unnamed variant can't have discriminant",
167 )
168 .into_compile_error()
169 .into();
170 }
171 for unamed in fields.unnamed.iter() {
172 if unamed.ty.to_token_stream().to_string() != ty_str {
173 return Error::new(
174 fields.span(),
175 format!(
176 "Unnamed variant's field must be the same type as its repr: {}",
177 ty.into_token_stream()
178 ),
179 )
180 .into_compile_error()
181 .into();
182 }
183 }
184
185 let mut range_cnt = 0;
186
187 for attr in v.attrs.iter() {
188 if attr.path().is_ident("range") {
189 range_cnt += 1;
190 if range_cnt > 1 {
191 return Error::new(
192 attr.path().span(),
193 "Only one range attribute must be provided for Unnamed variant",
194 )
195 .into_compile_error()
196 .into();
197 }
198 let range: ExprRange = match attr.parse_args() {
199 Ok(r) => r,
200 Err(e) => return e.into_compile_error().into(),
201 };
202
203 if let Err(e) = checker.substract(&range) {
207 return e.into_compile_error().into();
208 }
209 let inclusive_expr = checker.expr_to_inclusive_expr(&range).unwrap();
210
211 ranges.push(range);
212 unnamed_variants.push(&v.ident);
213 unnamed_ranges.push(inclusive_expr);
214 }
215 }
216 if range_cnt != 1 {
217 return Error::new(
218 fields.span(),
219 "one range attribute must be provided for Unnamed variant",
220 )
221 .into_compile_error()
222 .into();
223 }
224 }
225 syn::Fields::Unit => {
227 if v.attrs.iter().any(|attr| attr.path().is_ident("range")) {
228 return Error::new(v.span(), "Unit variant should not have `range` attribute")
229 .into_compile_error()
230 .into();
231 }
232 match v.discriminant.as_ref() {
236 Some((_, n)) => {
237 let s = n.to_token_stream().to_string();
238 let range =
239 syn::parse_str::<ExprRange>(format!("{}..={}", s, s).as_str()).unwrap();
240 if let Err(e) = checker.substract(&range) {
241 return Error::new(n.span(), e.to_string())
243 .into_compile_error()
244 .into();
245 }
246 ranges.push(range);
247 unit_discriminant.push(n);
248 unit_variants.push(&v.ident);
249 }
250 None => {
251 return Error::new(v.span(), "must specify discriminant, like A=0")
252 .into_compile_error()
253 .into()
254 }
255 }
256 }
257 }
258 }
260
261 let all_ranges = ranges
271 .iter()
272 .map(|v| checker.expr_to_inclusive_expr(v).unwrap())
273 .collect::<Vec<_>>();
274
275 let mut token_stream = quote! {
276 impl From<#ident> for #ty {
277 fn from(value: #ident) -> Self {
278 match value {
279 #(
280 #ident::#unit_variants => #unit_discriminant,
281 )*
282 #(
283 #ident::#unnamed_variants(n) => n,
284 )*
285 }
286 }
287 }
288
289 impl #ident {
315 pub fn ranges() -> &'static [core::ops::RangeInclusive<#ty>] {
316 &[#(#all_ranges,)*]
317 }
318 pub fn is_valid(&self) -> bool {
319 match self {
320 #(
321 Self::#unit_variants => true,
322 )*
323 #(
324 #[allow(unreachable_patterns)]
325 #[allow(non_contiguous_range_endpoints)]
326 Self::#unnamed_variants(n) => match n {
327 #unnamed_ranges => true,
328 _ => false,
329 },
330 )*
331 }
332 }
333 }
334 };
335
336 let ty_to_ident = if checker.is_empty() {
337 quote! {
338 impl From<#ty> for #ident {
339 fn from(value: #ty) -> Self {
340 match value {
341 #(
342 #unit_discriminant => Self::#unit_variants,
343 )*
344 #(
345 #unnamed_ranges => Self::#unnamed_variants(value),
346 )*
347 }
348 }
349 }
350 }
351 } else {
352 quote! {
353 impl TryFrom<#ty> for #ident {
354 type Error = #ty;
355
356 fn try_from(value: #ty) -> Result<Self, Self::Error> {
357 #[allow(unreachable_patterns)]
358 #[allow(non_contiguous_range_endpoints)]
359 match value {
360 #(
361 #unit_discriminant => Ok(Self::#unit_variants),
362 )*
363 #(
364 #unnamed_ranges => Ok(Self::#unnamed_variants(value)),
365 )*
366 _ => Err(value)
367 }
368 }
369 }
370 }
371 };
372
373 token_stream.extend(ty_to_ident);
374 token_stream.into()
375}