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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use crate::primitives::Primitive;
use quote::{quote, ToTokens};
use std::{collections::BTreeMap, hash::Hash};
use syn::Item;

mod cargo_dependency;
mod custom_type;
mod enums;
mod structs;
mod type_ident;

pub use cargo_dependency::CargoDependency;
pub use custom_type::CustomType;
pub use enums::{Enum, EnumOptions, Variant, VariantAttrs};
pub use structs::{Field, FieldAttrs, Struct, StructOptions};
pub use type_ident::TypeIdent;

pub type TypeMap = BTreeMap<TypeIdent, Type>;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
    Alias(String, TypeIdent),
    Container(String, TypeIdent),
    Custom(CustomType),
    Enum(Enum),
    List(String, TypeIdent),
    Map(String, TypeIdent, TypeIdent),
    Primitive(Primitive),
    String,
    Struct(Struct),
    Tuple(Vec<TypeIdent>),
    Unit,
}

impl Type {
    pub fn from_item(item_str: &str) -> Self {
        let item = syn::parse_str::<Item>(item_str).unwrap();
        match item {
            Item::Enum(item) => Type::Enum(enums::parse_enum_item(item)),
            Item::Struct(item) => Type::Struct(structs::parse_struct_item(item)),
            item => panic!(
                "Only struct and enum types can be constructed from an item. Found: {:?}",
                item
            ),
        }
    }

    pub fn name(&self) -> String {
        match self {
            Self::Alias(name, _) => name.clone(),
            Self::Container(name, ident) => format!("{}<{}>", name, ident),
            Self::Custom(custom) => custom.ident.to_string(),
            Self::Enum(Enum { ident, .. }) => ident.to_string(),
            Self::List(name, ident) => format!("{}<{}>", name, ident),
            Self::Map(name, key, value) => format!("{}<{}, {}>", name, key, value),
            Self::Primitive(primitive) => primitive.name(),
            Self::String => "String".to_owned(),
            Self::Struct(Struct { ident, .. }) => ident.to_string(),
            Self::Tuple(items) => format!(
                "({})",
                items
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
            Self::Unit => "()".to_owned(),
        }
    }
}

impl ToTokens for Type {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        (match self {
            Type::Alias(name, _) | Type::Custom(CustomType { rs_ty: name, .. }) => {
                let ty = syn::parse_str::<syn::Type>(name).unwrap();
                quote! { #ty }
            }
            Type::Container(name, ident) | Type::List(name, ident) => {
                let name = syn::parse_str::<syn::Type>(name).unwrap();
                quote! { #name<#ident> }
            }
            Type::Struct(Struct { ident, .. }) | Type::Enum(Enum { ident, .. }) => {
                quote! { #ident }
            }
            Type::Map(name, k, v) => {
                let name = syn::parse_str::<syn::Type>(name).unwrap();
                quote! { #name<#k, #v> }
            }
            Type::Primitive(primitive) => quote! { #primitive },
            Type::String => quote! { String },
            Type::Tuple(items) => quote! { (#(#items),*) },
            Type::Unit => quote! { () },
        })
        .to_tokens(tokens)
    }
}