error_chain_utils_lib/
quick.rs

1//! This module implements the `error_chain_quick!` macro for `error-chain-utils`
2//! See the full documentation there
3
4use std::fmt;
5use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
6use syn::{parse::{Parse, ParseStream, ParseBuffer}, parse2};
7use quote::{quote,ToTokens};
8
9trait TryParse {
10    fn try_parse<T: Parse>(&self) -> syn::Result<T>;
11}
12
13impl TryParse for ParseBuffer<'_> {
14    fn try_parse<T: Parse>(&self) -> syn::Result<T> {
15        match self.fork().parse::<T>() {
16            Ok(_) => Ok(self.parse::<T>()?),
17            Err(e) => Err(e)
18        }
19    }
20}
21
22trait ProcessQuickError<T> {
23    fn process_quick_error(self) -> T;
24}
25
26mod errors_child_element {
27    use std::fmt;
28    use syn::{LitStr, parenthesized, parse::{Parse, ParseStream, ParseBuffer}, token, punctuated};
29    use proc_macro2::{Delimiter, Group, Ident, TokenStream, TokenTree};
30    use quote::{ToTokens, quote};
31    use crate::quick::{ProcessQuickError,TryParse};
32
33    #[derive(Debug)]
34    pub struct NormalError {
35        ident: Ident,
36        args: Option<Group>,
37        body: Group
38    }
39
40    impl Parse for NormalError {
41        fn parse(input: ParseStream) -> syn::Result<Self> {
42            let ident = input.try_parse::<Ident>()?;
43            let first_group = input.try_parse::<Group>()?;
44            if first_group.delimiter() == Delimiter::Parenthesis {
45                let second_group = input.try_parse::<Group>()?;
46                if second_group.delimiter() == Delimiter::Brace {
47                    Ok(NormalError {
48                        ident,
49                        args: Some(first_group),
50                        body: second_group
51                    })
52                } else {
53                    Err(syn::Error::new(second_group.span_open(),"Unexpected Delimiter Here"))
54                }
55            } else if first_group.delimiter() == Delimiter::Brace {
56                Ok(NormalError {
57                    ident,
58                    args: None,
59                    body: first_group
60                })
61            } else {
62                Err(syn::Error::new(first_group.span_open(), "Unexpected delimiter here"))
63            }
64        }
65    }
66
67    impl ToTokens for NormalError {
68        fn to_tokens(&self, tokens: &mut TokenStream) {
69            tokens.extend_one(TokenTree::from(self.ident.clone()));
70            match self.args.clone() {
71                Some(val) => tokens.extend_one(TokenTree::from(val)),
72                _ => ()
73            };
74            tokens.extend_one(TokenTree::from(self.body.clone()));
75        }
76    }
77
78    pub struct QuickError {
79        err_ident: Ident,
80        desc: LitStr,
81        inner_args: punctuated::Punctuated<Ident,token::Comma>
82    }
83
84    impl fmt::Debug for QuickError {
85        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(),fmt::Error> {
86
87            struct LitStrDebug<'a> {
88                inner: &'a LitStr
89            }
90
91            impl fmt::Debug for LitStrDebug<'_> {
92                fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(),fmt::Error> {
93                    fmt.write_str("LitStr( ")?;
94                    self.inner.value().fmt(fmt)?;
95                    fmt.write_str(" )")?;
96                    Ok(())
97                }
98            }
99
100            struct PunctuatedDebug<'a,T,U> {
101                inner: &'a punctuated::Punctuated<T,U>
102            }
103    
104            impl<T: fmt::Debug,U> fmt::Debug for PunctuatedDebug<'_,T,U> {
105                fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(),fmt::Error> {
106                    fmt.write_str("Punctuated( ")?;
107                    self.inner.iter().collect::<Vec<&T>>().fmt(fmt)?;
108                    fmt.write_str(" )")?;
109                    Ok(())
110                }
111            }
112    
113            #[derive(Debug)]
114            struct QuickErrorDebug<'a> {
115                err_ident: &'a Ident,
116                desc: LitStrDebug<'a>,
117                inner_args: PunctuatedDebug<'a,Ident,token::Comma>
118            }
119    
120            (QuickErrorDebug { 
121                err_ident: &self.err_ident,
122                desc: LitStrDebug { inner: &self.desc },
123                inner_args: PunctuatedDebug {
124                    inner: &self.inner_args
125                }
126            }).fmt(fmt)
127        }
128
129    }
130
131    fn parse_parens(input: ParseStream) -> syn::Result<ParseBuffer> {
132        let contents;
133        parenthesized!(contents in input);
134        Ok(contents)
135    }
136
137    fn try_parse_parens(input: ParseStream) -> syn::Result<ParseBuffer> {
138        match parse_parens(&mut input.fork()) {
139            Ok(_) => Ok(parse_parens(input).unwrap()),
140            Err(e) => Err(e.clone()) 
141        }
142    }
143
144    impl Parse for QuickError {
145        fn parse(input: ParseStream) -> syn::Result<Self> {
146            let ident = input.try_parse::<Ident>()?;
147            (ident.to_string() == "quick")
148                .then_some(())
149                .ok_or(syn::Error::new(ident.span(),"Ident was not 'quick'"))?;
150            input.try_parse::<token::Bang>()?;
151            let late_fail: Result<QuickError,syn::Error> = try {
152                let args = &mut try_parse_parens(input)?;
153                let err_ident = args.try_parse::<Ident>()?;
154                args.try_parse::<token::Comma>()?;
155                let desc = args.try_parse::<LitStr>()?;
156                let mut invalid_inner_args = false;
157                let optional_paren: syn::Result<punctuated::Punctuated<Ident,token::Comma>> = try{
158                    args.try_parse::<token::Comma>()?;
159                    let inner_args_unparsed = &mut try_parse_parens(args)?;
160                    match punctuated::Punctuated::parse_terminated(&inner_args_unparsed) {
161                        Ok(val) => Ok(val),
162                        Err(e) => {
163                            invalid_inner_args = true;
164                            Err(e)
165                        }
166                    }?
167                };
168                let inner_args = match optional_paren {
169                    Ok(val) => Ok(val),
170                    Err(_) => {
171                        if invalid_inner_args {
172                            Err(syn::Error::new(args.span(),"INV_QUICK"))
173                        } else {
174                            Ok(punctuated::Punctuated::new())
175                        }
176                    }
177                };
178
179                match args.try_parse::<token::Comma>() { _ => ()};
180                args.is_empty().then_some(()).ok_or(syn::Error::new(args.span(),"INV_QUICK"))?;
181
182                QuickError {
183                    err_ident,
184                    desc,
185                    inner_args: inner_args?
186                }
187            };
188            late_fail.or_else(|i:syn::Error| {Err(syn::Error::new(i.span(),"INV_QUICK"))})
189            
190        }
191    }
192
193    impl ProcessQuickError<NormalError> for QuickError {
194        fn process_quick_error(self) -> NormalError {
195            let ident = self.err_ident;
196            let mut args_token_stream = TokenStream::new();
197            let mut first_arg = true;
198            for arg in self.inner_args.clone() {
199                if first_arg {
200                    first_arg = false;
201                } else {
202                    args_token_stream.extend(quote!(, ));
203                }
204                args_token_stream.extend_one(TokenTree::from(arg));
205                args_token_stream.extend(quote!( : String));
206            }
207            let args;
208            let are_args_empty = args_token_stream.is_empty();
209            if are_args_empty {
210                args = None;
211            } else {
212                args = Some(Group::new(Delimiter::Parenthesis,args_token_stream));
213            }
214
215            let mut body_token_stream = TokenStream::new();
216            body_token_stream.extend(quote!(description));
217            body_token_stream.extend_one(TokenTree::from(Group::new(Delimiter::Parenthesis,self.desc.to_token_stream())));
218            body_token_stream.extend(quote!(display));
219            
220            let mut display_args_token_stream = TokenStream::new();
221            if are_args_empty {
222                display_args_token_stream.extend(self.desc.to_token_stream());
223            } else {
224                let mut format_str: String = self.desc.value();
225                format_str += ":";
226                for _ in self.inner_args.clone() {
227                    format_str += " {},"
228                }
229                format_str.pop();
230                display_args_token_stream.extend_one(LitStr::new(format_str.as_str(),self.desc.span()).into_token_stream());
231            }
232
233            for arg in self.inner_args {
234                display_args_token_stream.extend(quote!(,));
235                display_args_token_stream.extend_one(TokenTree::from(arg));
236            }
237
238            body_token_stream.extend_one(TokenTree::from(Group::new(Delimiter::Parenthesis,display_args_token_stream)));
239            let body = Group::new(Delimiter::Brace,body_token_stream);
240            NormalError { ident, args, body }
241        }
242    }
243}
244
245#[derive(Debug)]
246enum ErrorsChildElementEnum {
247    QuickError(errors_child_element::QuickError),
248    NormalError(errors_child_element::NormalError)
249}
250
251impl Parse for ErrorsChildElementEnum {
252    fn parse(input: ParseStream) -> syn::Result<Self> {
253        match input.try_parse() as syn::Result<errors_child_element::QuickError> {
254            Ok(val) => return Ok(Self::QuickError(val)),
255            Err(e) => {
256                if e.to_string() == "INV_QUICK" {
257                    return Err(e)
258                } else {
259                    ()
260                }
261            }
262        };
263        match input.try_parse() as syn::Result<errors_child_element::NormalError> {
264            Ok(val) => return Ok(Self::NormalError(val)),
265            Err(_) => ()
266        };
267        Err(input.error("Could not parse as ErrorsChildElementEnum"))
268    }
269}
270
271impl ProcessQuickError<ErrorsChildElementEnum> for ErrorsChildElementEnum {
272    fn process_quick_error(self) -> ErrorsChildElementEnum {
273        match self {
274            Self::QuickError(val) => Self::NormalError(val.process_quick_error()),
275            _ => self
276        }
277    }
278}
279
280impl ToTokens for ErrorsChildElementEnum {
281    fn to_tokens(&self, tokens: &mut TokenStream) {
282        match self {
283            Self::NormalError(ref val) => val.to_tokens(tokens),
284            Self::QuickError(ref _val) => panic!("Not all QuickError structs were converted to NormalError ones")
285        }
286    }
287}
288
289mod root_element {
290    use quote::ToTokens;
291    use syn::{braced, parse::{Parse, ParseStream}};
292    use proc_macro2::{Delimiter, Group, Ident, TokenStream, TokenTree};
293    use crate::quick::{ErrorsChildElementEnum, ProcessQuickError, TryParse};
294
295    #[derive(Debug)]
296    pub struct ErrorsIdGroup {
297        ident: Ident,
298        items: Vec<ErrorsChildElementEnum>
299    }
300    
301    impl Parse for ErrorsIdGroup {
302        fn parse(input: ParseStream) -> syn::Result<Self> {
303            let ident = input.try_parse::<Ident>()?;
304            if ident.to_string() == "errors" {
305                let errors;
306                braced!(errors in input);
307                let mut items = vec![];
308                if errors.is_empty() {
309                    Err(errors.error("Unexpected end of input"))
310                } else {
311                    while !errors.is_empty() {
312                        items.push(errors.try_parse::<ErrorsChildElementEnum>()?);
313                    }
314                    Ok(ErrorsIdGroup {
315                        ident,
316                        items
317                    })
318                }
319            } else {
320                Err(syn::Error::new(ident.span(),"Expected 'errors'"))
321            }
322        }
323    }
324
325    impl ProcessQuickError<ErrorsIdGroup> for ErrorsIdGroup {
326        fn process_quick_error(self) -> ErrorsIdGroup {
327            let mut new_items = vec![];
328            for item in self.items {
329                new_items.push(item.process_quick_error());
330            }
331            ErrorsIdGroup {
332                ident: self.ident,
333                items: new_items
334            }
335        }
336    }
337
338    impl ToTokens for ErrorsIdGroup {
339        fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
340            tokens.extend_one(TokenTree::from(self.ident.clone()));
341            let mut group_token_stream = TokenStream::new();
342            for item in &self.items {
343                item.to_tokens(&mut group_token_stream);
344            }
345            tokens.extend_one(TokenTree::from(Group::new(Delimiter::Brace,group_token_stream)));
346        }
347    }
348
349    #[derive(Debug)]
350    pub struct OtherIdGroup {
351        ident: Ident,
352        body: Option<Group>
353    }
354    
355    impl Parse for OtherIdGroup {
356        fn parse(input: ParseStream) -> syn::Result<Self> {
357            let ident = input.try_parse::<Ident>()?;
358            let body = input.try_parse() as syn::Result<Group>;
359            match body {
360                Ok(val) => Ok(OtherIdGroup {
361                    ident,
362                    body: Some(val)
363                }),
364                Err(_) => Ok(OtherIdGroup {
365                    ident,
366                    body: None
367                })
368            }
369        }
370    }
371
372    impl ToTokens for OtherIdGroup {
373        fn to_tokens(&self, tokens: &mut TokenStream) {
374            tokens.extend_one(TokenTree::from(self.ident.clone()));
375            match self.body.clone() {
376                Some(val) => tokens.extend_one(TokenTree::from(val)),
377                None => ()
378            };
379        }
380    }
381}
382
383#[derive(Debug)]
384enum RootElementEnum {
385    ErrorsIdGroup(root_element::ErrorsIdGroup),
386    OtherIdGroup(root_element::OtherIdGroup)
387}
388
389impl Parse for RootElementEnum {
390    fn parse(input: ParseStream) -> syn::Result<Self> {
391        match input.try_parse() as syn::Result<root_element::ErrorsIdGroup> {
392            Ok(val) => Ok(RootElementEnum::ErrorsIdGroup(val)),
393            Err(e) => {
394                if e.to_string() == "INV_QUICK" {
395                    return Err(e);
396                }
397                match input.try_parse() as syn::Result<root_element::OtherIdGroup> {
398                    Ok(val) => Ok(RootElementEnum::OtherIdGroup(val)),
399                    Err(e) => Err(e)
400                }
401            }
402        }
403    }
404}
405
406impl ProcessQuickError<RootElementEnum> for RootElementEnum {
407    fn process_quick_error(self) -> RootElementEnum {
408        match self {
409            Self::ErrorsIdGroup(val) => Self::ErrorsIdGroup(val.process_quick_error()),
410            _ => self
411        }
412    }
413}
414
415impl ToTokens for RootElementEnum {
416    fn to_tokens(&self, tokens: &mut TokenStream) {
417        match self {
418            Self::ErrorsIdGroup(ref val) => val.to_tokens(tokens),
419            Self::OtherIdGroup(ref val)  => val.to_tokens(tokens)
420        }
421    }
422}
423
424struct RootElementVec {
425    items: Vec<RootElementEnum>
426}
427
428impl fmt::Debug for RootElementVec {
429    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
430        self.items.fmt(f)
431    }
432}
433
434impl Parse for RootElementVec {
435    fn parse(input: ParseStream) -> syn::Result<Self> {
436        let mut items = vec![];
437        if input.is_empty() {
438            Err(input.error("Unexpected end of input"))
439        } else {
440            while !input.is_empty() {
441                items.push(RootElementEnum::parse(input)?);
442            }
443            Ok(RootElementVec {
444                items
445            })
446        }
447    }
448}
449
450impl ProcessQuickError<RootElementVec> for RootElementVec {
451    fn process_quick_error(self) -> RootElementVec {
452        let mut new_items = vec![];
453        for item in self.items {
454            new_items.push(item.process_quick_error());
455        }
456        RootElementVec {
457            items: new_items
458        }
459    }
460}
461
462impl ToTokens for RootElementVec {
463    fn to_tokens(&self, tokens: &mut TokenStream) {
464        let mut group_token_stream = TokenStream::new();
465        for item in &self.items {
466            item.to_tokens(&mut group_token_stream);
467        }
468        tokens.extend_one(TokenTree::from(Group::new(Delimiter::Brace,group_token_stream)));
469    }
470}
471
472
473
474/// Main function for the [`error_chain_quick!`](../../error_chain_utils/macro.error_chain_quick.html) macro
475pub fn main(input: TokenStream) -> syn::Result<TokenStream> {
476    let parsed_input: RootElementVec = match parse2(input) {
477        Ok(val) => val,
478        Err(e) => {
479            if e.to_string() == "INV_QUICK" {
480                let mut new_e = syn::Error::new(e.span(),"Invalid 'quick!()' macro");
481                new_e.combine(e);
482                return Err(new_e);
483            } else {
484                return Err(e);
485            }
486        }
487    };
488    let transformed_input: RootElementVec = parsed_input.process_quick_error();
489    let mut output_stream: TokenStream = TokenStream::new();
490    output_stream.extend(quote!(::error_chain::error_chain!));
491    transformed_input.to_tokens(&mut output_stream);
492    Ok(output_stream)
493}
494
495
496#[cfg(test)]
497mod tests{
498    use std::assert_eq;
499    use quote::quote;
500    use crate::quick;
501    #[test]
502    pub fn test() {
503        let input = quote!{
504
505            types {
506                BuildError, BEKind, BETrait, BEResult;
507            }
508        
509            errors {
510                NormalError1 {
511                    description("Error 1 Description: Without Arguments"),
512                    display("Error 1 Display")
513                }
514                NormalError2 (arg1: String, arg2: String) {
515                    description("Error 2 Description: With Arguments"),
516                    display("Error 2 Display: {}, {}", arg1, arg2),
517                }
518                quick!(QuickError1, "Error 1 Description: Zero arguments")
519                quick!(QuickError2, "Error 2 Description: One Argument",(arg1,))
520                quick!(QuickError3, "Error 3 Description: Three Arguments",(arg1,arg2,arg3,))
521                quick!(QuickError4, "Error 4 Description: Zero arguments, trailing comma",)
522            }
523        };
524        let output = quick::main(input).unwrap();
525        let expected_output = quote!{
526            ::error_chain::error_chain!{
527                types {
528                    BuildError, BEKind, BETrait, BEResult;
529                }
530                
531                errors {
532                    NormalError1 {
533                        description("Error 1 Description: Without Arguments"),
534                        display("Error 1 Display")
535                    }
536                    NormalError2 (arg1: String , arg2: String){
537                        description("Error 2 Description: With Arguments"),
538                        display("Error 2 Display: {}, {}", arg1, arg2),
539                    }
540                    QuickError1 {
541                        description("Error 1 Description: Zero arguments")
542                        display ("Error 1 Description: Zero arguments")
543                    }
544                    QuickError2 (arg1: String){
545                        description("Error 2 Description: One Argument")
546                        display("Error 2 Description: One Argument: {}", arg1)
547                    }
548                    QuickError3 (arg1: String, arg2: String, arg3: String){
549                        description("Error 3 Description: Three Arguments")
550                        display("Error 3 Description: Three Arguments: {}, {}, {}", arg1, arg2, arg3)
551                    }
552                    QuickError4 {
553                        description("Error 4 Description: Zero arguments, trailing comma")
554                        display ("Error 4 Description: Zero arguments, trailing comma")
555                    }
556                }
557            }
558        };
559        assert_eq!(output.to_string(),expected_output.to_string(),"Actual output and Expected output did not match.\n Expected Output: \n{:#?}\n Actual Output: \n{:#?}\n",expected_output,output);
560    }
561}