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 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}