autoerr_impl/
lib.rs

1mod expand;
2
3use proc_macro2::Group;
4use proc_macro2::Ident;
5use proc_macro2::Punct;
6use proc_macro2::Span;
7use proc_macro2::TokenStream;
8use proc_macro2::TokenTree;
9use quote::quote;
10use syn::parse_macro_input;
11use syn::DeriveInput;
12
13struct VariantFieldGroupBasic {
14    tys: Vec<syn::Path>,
15}
16
17impl syn::parse::Parse for VariantFieldGroupBasic {
18    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
19        let mut tys = Vec::new();
20        let ty: syn::Path = input.parse()?;
21        tys.push(ty.clone());
22        loop {
23            if !input.peek(syn::token::Comma) {
24                break;
25            }
26            let _p: Punct = input.parse()?;
27            let ty: syn::Path = input.parse()?;
28            tys.push(ty);
29        }
30        let ret = Self { tys };
31        Ok(ret)
32    }
33}
34
35struct VariantFieldGroupFrom {
36    tys: Vec<syn::Path>,
37}
38
39impl syn::parse::Parse for VariantFieldGroupFrom {
40    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
41        let _p: Punct = input.parse()?;
42        let _gr: proc_macro2::Group = input.parse()?;
43        // let name: Ident = input.parse()?;
44        let mut tys = Vec::new();
45        let ty: syn::Path = input.parse()?;
46        tys.push(ty.clone());
47        loop {
48            if !input.peek(syn::token::Comma) {
49                break;
50            }
51            let _p: Punct = input.parse()?;
52            let ty: syn::Path = input.parse()?;
53            tys.push(ty);
54        }
55        let ret = Self { tys };
56        Ok(ret)
57    }
58}
59
60enum VariantFieldGroup {
61    Plain,
62    Basic(VariantFieldGroupBasic),
63    From(VariantFieldGroupFrom),
64}
65
66impl syn::parse::Parse for VariantFieldGroup {
67    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
68        if input.peek(syn::token::Pound) {
69            let v = input.parse()?;
70            let ret = Self::From(v);
71            Ok(ret)
72        } else {
73            let v = input.parse()?;
74            let ret = Self::Basic(v);
75            Ok(ret)
76        }
77    }
78}
79
80struct ErrorVariant {
81    name: Ident,
82    field_group: VariantFieldGroup,
83}
84
85struct MacroInput2 {
86    errname: Ident,
87    errdesc: syn::LitStr,
88    variants: Vec<ErrorVariant>,
89}
90
91impl syn::parse::Parse for MacroInput2 {
92    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
93        let mut tokens = Vec::new();
94        let a: Ident = input.parse()?;
95        let b: Punct = input.parse()?;
96        if a != "name" {
97            panic!("expect name")
98        }
99        if b.as_char() != ':' {
100            panic!("expect colon")
101        }
102        // let _: syn::LitStr = input.parse()?;
103        let errname: syn::Ident = input.parse()?;
104        let _: Punct = input.parse()?;
105        let a: Ident = input.parse()?;
106        let b: Punct = input.parse()?;
107        if a != "txt" {
108            panic!("expect txt")
109        }
110        if b.as_char() != ':' {
111            panic!("expect colon")
112        }
113        let errdesc: syn::LitStr = input.parse()?;
114        let _p: Punct = input.parse()?;
115        let n: Ident = input.parse()?;
116        let p: Punct = input.parse()?;
117        if p.as_char() != ':' {
118            panic!("expect colon")
119        }
120        let mut variants = None;
121        if n == "variants" {
122            let gr: proc_macro2::Group = input.parse()?;
123            let p: Punct = input.parse()?;
124            if p.as_char() != ';' {
125                panic!("expect semicolon")
126            }
127            let mut vars = Vec::new();
128            let mut curr: Option<ErrorVariant> = None;
129            let mut finish_curr = |curr| {
130                vars.push(curr);
131            };
132            let stream = gr.stream();
133            for token in stream {
134                match token {
135                    TokenTree::Group(x) => {
136                        tokens.push(format!("GROUP[{}]", x));
137                        let fg: VariantFieldGroup = syn::parse(x.stream().into())?;
138                        let curr = curr.as_mut().unwrap();
139                        curr.field_group = fg;
140                    }
141                    TokenTree::Ident(x) => {
142                        tokens.push(format!("IDENT[{}]", x));
143                        if let Some(_curr) = curr.as_mut() {
144                            panic!("variant already in progress");
145                        } else {
146                            let v = ErrorVariant {
147                                name: x,
148                                field_group: VariantFieldGroup::Plain,
149                            };
150                            curr = Some(v);
151                        }
152                    }
153                    TokenTree::Punct(x) => {
154                        tokens.push(format!("PUNCT[{}]", x));
155                        if let Some(curr) = curr.take() {
156                            finish_curr(curr);
157                        }
158                    }
159                    TokenTree::Literal(x) => {
160                        tokens.push(format!("LITERAL[{}]", x));
161                    }
162                }
163            }
164            if let Some(curr) = curr.take() {
165                finish_curr(curr);
166            }
167            variants = Some(vars);
168        }
169        // compile_error!("sdfsdf");
170        let ret = Self {
171            errname,
172            errdesc,
173            variants: variants.expect("error variants"),
174        };
175        Ok(ret)
176    }
177}
178
179#[proc_macro]
180pub fn create_error_v2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
181    let input = parse_macro_input!(input as MacroInput2);
182    let errname = input.errname.clone();
183    let errdesc = input.errdesc.clone();
184    let errname_litstr = syn::LitStr::new(&input.errname.to_string(), Span::call_site());
185    let mut vars_ts = TokenStream::new();
186    let mut froms_ts = TokenStream::new();
187    let mut fmtbranches_ts = TokenStream::new();
188    for v in input.variants {
189        let vn = &v.name;
190        let vn_litstr = syn::LitStr::new(&vn.to_string(), Span::call_site());
191        match v.field_group {
192            VariantFieldGroup::Plain => {
193                let q = quote! {
194                    #vn,
195                };
196                vars_ts.extend(q);
197                let q = quote! {
198                    #errname::#vn => ::core::write!(fmt, "{}::{}", #errname_litstr, #vn_litstr),
199                };
200                fmtbranches_ts.extend(q);
201            }
202            VariantFieldGroup::Basic(x) => {
203                let tys = &x.tys;
204                let q = quote! {
205                    #vn(#(#tys),*),
206                };
207                vars_ts.extend(q);
208                let dvs: Vec<_> = x
209                    .tys
210                    .iter()
211                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
212                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
213                    .collect();
214                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
215                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
216                let q = quote! {
217                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname_litstr, #vn_litstr, #(#dvs),*),
218                };
219                fmtbranches_ts.extend(q);
220            }
221            VariantFieldGroup::From(x) => {
222                let tys = &x.tys;
223                let q = quote! {
224                    #vn(#(#tys),*),
225                };
226                vars_ts.extend(q);
227
228                let dvs: Vec<_> = x
229                    .tys
230                    .iter()
231                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
232                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
233                    .collect();
234                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
235                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
236                let q = quote! {
237                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname_litstr, #vn_litstr, #(#dvs),*),
238                };
239                fmtbranches_ts.extend(q);
240
241                let ty = &x.tys[0];
242                let q = quote! {
243                    impl From<#ty> for #errname {
244                        fn from(value: #ty) -> Self {
245                            Self::#vn(value)
246                        }
247                    }
248                };
249                froms_ts.extend(q);
250            }
251        }
252    }
253    let ret = quote! {
254        #[derive(Debug)]
255        pub enum #errname {
256            #vars_ts
257        }
258
259        impl #errname {
260            fn desc() -> &'static str {
261                #errdesc
262            }
263        }
264
265        #froms_ts
266
267        impl ::core::fmt::Display for #errname {
268            fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
269                match self {
270                    #fmtbranches_ts
271                }
272            }
273        }
274
275        impl ::core::error::Error for #errname {}
276
277        #[allow(non_snake_case)]
278        fn show() -> &'static str {
279            ""
280        }
281    };
282
283    ret.into()
284}
285
286struct TyNameSeq {
287    name: Ident,
288    desc: syn::LitStr,
289}
290
291impl syn::parse::Parse for TyNameSeq {
292    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
293        let name: Ident = input.parse()?;
294        let _p: syn::token::Comma = input.parse()?;
295        let desc: syn::LitStr = input.parse()?;
296        let ret = Self { name, desc };
297        Ok(ret)
298    }
299}
300
301struct MacroInput {
302    name: TyNameSeq,
303    variants: Vec<ErrorVariant>,
304}
305
306impl syn::parse::Parse for MacroInput {
307    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
308        // input.peek(syn::Token![]);
309        // input.peek(syn::Ident);
310        // input.peek(Ident::new("pubb", Span::mixed_site()));
311        if input.peek(syn::token::Pub) {
312            let _: syn::token::Pub = input.parse()?;
313            let _: syn::token::Comma = input.parse()?;
314        }
315        let a: Ident = input.parse()?;
316        let b: Group = input.parse()?;
317        if a != "name" {
318            panic!("expect name")
319        }
320        let name: TyNameSeq = syn::parse(b.stream().into())?;
321        let _: Punct = input.parse()?;
322        let _: syn::token::Enum = input.parse()?;
323        let n: Ident = input.parse()?;
324        let b: Group = input.parse()?;
325        let _: Punct = input.parse()?;
326        let mut variants = None;
327        if n == "variants" {
328            let gr: proc_macro2::Group = b;
329            let mut vars = Vec::new();
330            let mut curr: Option<ErrorVariant> = None;
331            let mut finish_curr = |curr| {
332                vars.push(curr);
333            };
334            let stream = gr.stream();
335            for token in stream {
336                match token {
337                    TokenTree::Group(x) => {
338                        let fg: VariantFieldGroup = syn::parse(x.stream().into())?;
339                        let curr = curr.as_mut().unwrap();
340                        curr.field_group = fg;
341                    }
342                    TokenTree::Ident(x) => {
343                        if let Some(_curr) = curr.as_mut() {
344                            panic!("variant already in progress");
345                        } else {
346                            let v = ErrorVariant {
347                                name: x,
348                                field_group: VariantFieldGroup::Plain,
349                            };
350                            curr = Some(v);
351                        }
352                    }
353                    TokenTree::Punct(_x) => {
354                        if let Some(curr) = curr.take() {
355                            finish_curr(curr);
356                        }
357                    }
358                    TokenTree::Literal(_x) => {}
359                }
360            }
361            if let Some(curr) = curr.take() {
362                finish_curr(curr);
363            }
364            variants = Some(vars);
365        }
366        // compile_error!("sdfsdf");
367        let ret = Self {
368            name,
369            variants: variants.expect("error variants"),
370        };
371        Ok(ret)
372    }
373}
374
375#[proc_macro]
376pub fn create_error_v1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
377    let input = parse_macro_input!(input as MacroInput);
378    let errname = input.name.name.clone();
379    let errdesc = input.name.desc.clone();
380    let mut vars_ts = TokenStream::new();
381    let mut froms_ts = TokenStream::new();
382    let mut fmtbranches_ts = TokenStream::new();
383    for v in input.variants {
384        let vn = &v.name;
385        let vn_litstr = syn::LitStr::new(&vn.to_string(), Span::call_site());
386        match v.field_group {
387            VariantFieldGroup::Plain => {
388                let q = quote! {
389                    #vn,
390                };
391                vars_ts.extend(q);
392                let q = quote! {
393                    #errname::#vn => ::core::write!(fmt, "{}::{}", #errname::txt(), #vn_litstr),
394                };
395                fmtbranches_ts.extend(q);
396            }
397            VariantFieldGroup::Basic(x) => {
398                let tys = &x.tys;
399                let q = quote! {
400                    #vn(#(#tys),*),
401                };
402                vars_ts.extend(q);
403                let dvs: Vec<_> = x
404                    .tys
405                    .iter()
406                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
407                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
408                    .collect();
409                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
410                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
411                let q = quote! {
412                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname::txt(), #vn_litstr, #(#dvs),*),
413                };
414                fmtbranches_ts.extend(q);
415            }
416            VariantFieldGroup::From(x) => {
417                let tys = &x.tys;
418                let q = quote! {
419                    #vn(#(#tys),*),
420                };
421                vars_ts.extend(q);
422
423                let dvs: Vec<_> = x
424                    .tys
425                    .iter()
426                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
427                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
428                    .collect();
429                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
430                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
431                let q = quote! {
432                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname::txt(), #vn_litstr, #(#dvs),*),
433                };
434                fmtbranches_ts.extend(q);
435
436                let ty = &x.tys[0];
437                let q = quote! {
438                    impl From<#ty> for #errname {
439                        fn from(value: #ty) -> Self {
440                            Self::#vn(value)
441                        }
442                    }
443                };
444                froms_ts.extend(q);
445            }
446        }
447    }
448    let show_ident = quote::format_ident!("show_{}", errname);
449    let ret = quote! {
450        #[derive(Debug)]
451        pub enum #errname {
452            #vars_ts
453        }
454
455        impl #errname {
456            fn txt() -> &'static str {
457                #errdesc
458            }
459        }
460
461        #froms_ts
462
463        impl ::core::fmt::Display for #errname {
464            fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
465                match self {
466                    #fmtbranches_ts
467                }
468            }
469        }
470
471        impl ::core::error::Error for #errname {}
472
473        #[allow(non_snake_case)]
474        fn #show_ident() -> &'static str {
475            ""
476        }
477    };
478
479    ret.into()
480}
481
482#[proc_macro_derive(Error, attributes(backtrace, error, from, source, cstm))]
483pub fn derive_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
484    let input = parse_macro_input!(input as DeriveInput);
485    let helpers = expand::DummyHelpers::new();
486    expand::derive(&input, &helpers).into()
487}