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        let mut tokens = Vec::new();
309        let a: Ident = input.parse()?;
310        let b: Group = input.parse()?;
311        if a != "name" {
312            panic!("expect name")
313        }
314        let name: TyNameSeq = syn::parse(b.stream().into())?;
315        let _: Punct = input.parse()?;
316        let _: syn::token::Enum = input.parse()?;
317        let n: Ident = input.parse()?;
318        let b: Group = input.parse()?;
319        let _: Punct = input.parse()?;
320        let mut variants = None;
321        if n == "variants" {
322            let gr: proc_macro2::Group = b;
323            let mut vars = Vec::new();
324            let mut curr: Option<ErrorVariant> = None;
325            let mut finish_curr = |curr| {
326                vars.push(curr);
327            };
328            let stream = gr.stream();
329            for token in stream {
330                match token {
331                    TokenTree::Group(x) => {
332                        tokens.push(format!("GROUP[{}]", x));
333                        let fg: VariantFieldGroup = syn::parse(x.stream().into())?;
334                        let curr = curr.as_mut().unwrap();
335                        curr.field_group = fg;
336                    }
337                    TokenTree::Ident(x) => {
338                        tokens.push(format!("IDENT[{}]", x));
339                        if let Some(_curr) = curr.as_mut() {
340                            panic!("variant already in progress");
341                        } else {
342                            let v = ErrorVariant {
343                                name: x,
344                                field_group: VariantFieldGroup::Plain,
345                            };
346                            curr = Some(v);
347                        }
348                    }
349                    TokenTree::Punct(x) => {
350                        tokens.push(format!("PUNCT[{}]", x));
351                        if let Some(curr) = curr.take() {
352                            finish_curr(curr);
353                        }
354                    }
355                    TokenTree::Literal(x) => {
356                        tokens.push(format!("LITERAL[{}]", x));
357                    }
358                }
359            }
360            if let Some(curr) = curr.take() {
361                finish_curr(curr);
362            }
363            variants = Some(vars);
364        }
365        // compile_error!("sdfsdf");
366        let ret = Self {
367            name,
368            variants: variants.expect("error variants"),
369        };
370        Ok(ret)
371    }
372}
373
374#[proc_macro]
375pub fn create_error_v1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
376    let input = parse_macro_input!(input as MacroInput);
377    let errname = input.name.name.clone();
378    let errdesc = input.name.desc.clone();
379    let mut vars_ts = TokenStream::new();
380    let mut froms_ts = TokenStream::new();
381    let mut fmtbranches_ts = TokenStream::new();
382    for v in input.variants {
383        let vn = &v.name;
384        let vn_litstr = syn::LitStr::new(&vn.to_string(), Span::call_site());
385        match v.field_group {
386            VariantFieldGroup::Plain => {
387                let q = quote! {
388                    #vn,
389                };
390                vars_ts.extend(q);
391                let q = quote! {
392                    #errname::#vn => ::core::write!(fmt, "{}::{}", #errname::txt(), #vn_litstr),
393                };
394                fmtbranches_ts.extend(q);
395            }
396            VariantFieldGroup::Basic(x) => {
397                let tys = &x.tys;
398                let q = quote! {
399                    #vn(#(#tys),*),
400                };
401                vars_ts.extend(q);
402                let dvs: Vec<_> = x
403                    .tys
404                    .iter()
405                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
406                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
407                    .collect();
408                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
409                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
410                let q = quote! {
411                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname::txt(), #vn_litstr, #(#dvs),*),
412                };
413                fmtbranches_ts.extend(q);
414            }
415            VariantFieldGroup::From(x) => {
416                let tys = &x.tys;
417                let q = quote! {
418                    #vn(#(#tys),*),
419                };
420                vars_ts.extend(q);
421
422                let dvs: Vec<_> = x
423                    .tys
424                    .iter()
425                    .zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
426                    .map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
427                    .collect();
428                let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
429                let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
430                let q = quote! {
431                    #errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname::txt(), #vn_litstr, #(#dvs),*),
432                };
433                fmtbranches_ts.extend(q);
434
435                let ty = &x.tys[0];
436                let q = quote! {
437                    impl From<#ty> for #errname {
438                        fn from(value: #ty) -> Self {
439                            Self::#vn(value)
440                        }
441                    }
442                };
443                froms_ts.extend(q);
444            }
445        }
446    }
447    let show_ident = quote::format_ident!("show_{}", errname);
448    let ret = quote! {
449        #[derive(Debug)]
450        pub enum #errname {
451            #vars_ts
452        }
453
454        impl #errname {
455            fn txt() -> &'static str {
456                #errdesc
457            }
458        }
459
460        #froms_ts
461
462        impl ::core::fmt::Display for #errname {
463            fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
464                match self {
465                    #fmtbranches_ts
466                }
467            }
468        }
469
470        impl ::core::error::Error for #errname {}
471
472        #[allow(non_snake_case)]
473        fn #show_ident() -> &'static str {
474            ""
475        }
476    };
477
478    ret.into()
479}
480
481#[proc_macro_derive(Error, attributes(backtrace, error, from, source, cstm))]
482pub fn derive_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
483    let input = parse_macro_input!(input as DeriveInput);
484    let helpers = expand::DummyHelpers::new();
485    expand::derive(&input, &helpers).into()
486}