ormlite_attr/
ttype.rs

1use crate::Ident;
2use proc_macro2::TokenStream;
3use quote::TokenStreamExt;
4use syn::PathArguments;
5
6#[derive(Clone, Debug, PartialEq, Hash)]
7pub enum Type {
8    Option(Box<Type>),
9    Vec(Box<Type>),
10    /// Database primitive, includes DateTime, Jsonb, etc.
11    Inner(InnerType),
12    Join(Box<Type>),
13}
14
15impl Type {
16    pub fn joined_type(&self) -> Option<&Type> {
17        match &self {
18            Type::Join(ty) => Some(ty.as_ref()),
19            _ => None,
20        }
21    }
22
23    pub fn is_string(&self) -> bool {
24        match self {
25            Type::Inner(ty) => ty.ident == "String",
26            _ => false,
27        }
28    }
29
30    pub fn is_json(&self) -> bool {
31        match self {
32            Type::Inner(ty) => ty.ident == "Json",
33            Type::Option(ty) => ty.is_json(),
34            _ => false,
35        }
36    }
37
38    pub fn is_join(&self) -> bool {
39        matches!(self, Type::Join(_))
40    }
41
42    pub fn is_option(&self) -> bool {
43        matches!(self, Type::Option(_))
44    }
45
46    pub fn inner_type_name(&self) -> String {
47        match self {
48            Type::Inner(ty) => ty.ident.to_string(),
49            Type::Option(ty) => ty.inner_type_name(),
50            Type::Vec(ty) => ty.inner_type_name(),
51            Type::Join(ty) => ty.inner_type_name(),
52        }
53    }
54
55    pub fn inner_type_mut(&mut self) -> &mut InnerType {
56        match self {
57            Type::Inner(ty) => ty,
58            Type::Option(ty) => ty.inner_type_mut(),
59            Type::Vec(ty) => ty.inner_type_mut(),
60            Type::Join(ty) => ty.inner_type_mut(),
61        }
62    }
63
64    pub fn inner_type(&self) -> &InnerType {
65        match self {
66            Type::Inner(ty) => ty,
67            Type::Option(ty) => ty.inner_type(),
68            Type::Vec(ty) => ty.inner_type(),
69            Type::Join(ty) => ty.inner_type(),
70        }
71    }
72
73    pub fn qualified_inner_name(&self) -> TokenStream {
74        match self {
75            Type::Inner(ty) => {
76                let segments = ty.path.iter();
77                let ident = &ty.ident;
78                quote::quote! {
79                    #(#segments)::* #ident
80                }
81            }
82            Type::Option(ty) => ty.qualified_inner_name(),
83            Type::Vec(ty) => ty.qualified_inner_name(),
84            Type::Join(ty) => ty.qualified_inner_name(),
85        }
86    }
87}
88
89impl From<InnerType> for Type {
90    fn from(value: InnerType) -> Self {
91        match value.ident.to_string().as_str() {
92            "Option" => {
93                let ty = value.args.unwrap();
94                Type::Option(Box::new(Type::from(*ty)))
95            }
96            "Vec" => {
97                let ty = value.args.unwrap();
98                Type::Vec(Box::new(Type::from(*ty)))
99            }
100            "Join" => {
101                let ty = value.args.unwrap();
102                Type::Join(Box::new(Type::from(*ty)))
103            }
104            _ => Type::Inner(value),
105        }
106    }
107}
108
109impl From<&syn::Path> for Type {
110    fn from(path: &syn::Path) -> Self {
111        let other = InnerType::from(path);
112        Type::from(other)
113    }
114}
115
116impl quote::ToTokens for Type {
117    fn to_tokens(&self, tokens: &mut TokenStream) {
118        match self {
119            Type::Option(ty) => {
120                tokens.append_all(quote::quote! { Option<#ty> });
121            }
122            Type::Vec(ty) => {
123                tokens.append_all(quote::quote! { Vec<#ty> });
124            }
125            Type::Inner(ty) => {
126                ty.to_tokens(tokens);
127            }
128            Type::Join(ty) => {
129                tokens.append_all(quote::quote! { ormlite::model::Join<#ty> });
130            }
131        }
132    }
133}
134
135impl PartialEq<&str> for Type {
136    fn eq(&self, other: &&str) -> bool {
137        let Type::Inner(t) = self else {
138            return false;
139        };
140        t.ident == other
141    }
142}
143
144#[derive(Clone, Debug, PartialEq, Eq, Hash)]
145pub struct InnerType {
146    pub path: Vec<Ident>,
147    pub ident: Ident,
148    pub args: Option<Box<InnerType>>,
149}
150
151impl InnerType {
152    #[doc(hidden)]
153    pub fn mock(ident: &str) -> Self {
154        Self {
155            path: vec![],
156            ident: Ident::from(ident),
157            args: None,
158        }
159    }
160}
161
162impl From<&syn::Path> for InnerType {
163    fn from(path: &syn::Path) -> Self {
164        let segment = path.segments.last().expect("path must have at least one segment");
165        let args: Option<Box<InnerType>> = if let PathArguments::AngleBracketed(args) = &segment.arguments {
166            let args = &args.args;
167            let syn::GenericArgument::Type(ty) = args.first().unwrap() else {
168                panic!("expected type syntax tree inside angle brackets");
169            };
170            let syn::Type::Path(path) = &ty else {
171                panic!("expected path syntax tree inside angle brackets");
172            };
173            Some(Box::new(InnerType::from(&path.path)))
174        } else {
175            None
176        };
177        let mut path = path.segments.iter().map(|s| Ident::from(&s.ident)).collect::<Vec<_>>();
178        let ident = path.pop().expect("path must have at least one segment");
179        InnerType { path, args, ident }
180    }
181}
182
183impl quote::ToTokens for InnerType {
184    fn to_tokens(&self, tokens: &mut TokenStream) {
185        let args = if let Some(args) = &self.args {
186            quote::quote! { <#args> }
187        } else {
188            quote::quote! {}
189        };
190        let path = &self.path;
191        let ident = &self.ident;
192        tokens.append_all(quote::quote! { #(#path ::)* #ident #args });
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_primitive() {
202        use syn::Path;
203        let ty = Type::from(&syn::parse_str::<Path>("i32").unwrap());
204        assert!(!ty.is_json());
205
206        let ty = Type::from(&syn::parse_str::<Path>("Json<User>").unwrap());
207        assert!(ty.is_json());
208    }
209
210    #[test]
211    fn test_other_type_to_quote() {
212        use syn::Path;
213        let ty = Type::from(&syn::parse_str::<Path>("rust_decimal::Decimal").unwrap());
214        let Type::Inner(ty) = &ty else {
215            panic!("expected primitive");
216        };
217        assert_eq!(ty.ident, "Decimal");
218        assert_eq!(ty.path.len(), 1);
219        assert_eq!(ty.path[0], "rust_decimal");
220        let z = quote::quote!(#ty);
221        assert_eq!(z.to_string(), "rust_decimal :: Decimal");
222    }
223}