1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{Error, GenericArgument, PathArguments, Result, Type};

pub enum OutputType<'a> {
    Value(&'a Type),
    Result(&'a Type, &'a Type),
}

impl<'a> OutputType<'a> {
    pub fn parse(input: &'a Type) -> Result<Self> {
        let ty = if let Type::Path(p) = input {
            if p.path.segments.last().unwrap().ident == "Result" {
                if let PathArguments::AngleBracketed(args) = &p.path.segments[0].arguments {
                    if args.args.is_empty() {
                        return Err(Error::new_spanned(input, "Invalid type"));
                    }
                    let mut res = None;
                    for arg in &args.args {
                        if let GenericArgument::Type(value_ty) = arg {
                            res = Some(OutputType::Result(input, value_ty));
                            break;
                        }
                    }
                    if res.is_none() {
                        return Err(Error::new_spanned(input, "Invalid type"));
                    }
                    res.unwrap()
                } else {
                    return Err(Error::new_spanned(input, "Invalid type"));
                }
            } else {
                OutputType::Value(input)
            }
        } else if let Type::Reference(_) = input {
            OutputType::Value(input)
        } else {
            return Err(Error::new_spanned(input, "Invalid type"));
        };
        Ok(ty)
    }

    pub fn value_type(&self) -> Type {
        let tokens = match self {
            OutputType::Value(ty) => quote! {#ty},
            OutputType::Result(_, ty) => quote! {#ty},
        };
        let mut ty = syn::parse2::<syn::Type>(tokens).unwrap();
        Self::remove_lifecycle(&mut ty);
        ty
    }

    fn remove_lifecycle(ty: &mut Type) {
        match ty {
            Type::Reference(r) => {
                r.lifetime = None;
                Self::remove_lifecycle(&mut r.elem);
            }
            Type::Path(r) => {
                for s in &mut r.path.segments {
                    if let PathArguments::AngleBracketed(args) = &mut s.arguments {
                        for arg in &mut args.args {
                            match arg {
                                GenericArgument::Lifetime(lt) => {
                                    lt.ident = Ident::new("_", Span::call_site());
                                }
                                GenericArgument::Type(ty) => {
                                    Self::remove_lifecycle(ty);
                                }
                                _ => {}
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
}