use super::{
ident_from_str, is_bytes, last_path_element, return_type, FieldContext, QuoteT, TSType,
};
use proc_macro2::Ident;
use quote::quote;
impl<'a> FieldContext<'a> {
#[allow(clippy::cyclomatic_complexity)]
fn generic_to_ts(&self, ts: &TSType) -> QuoteT {
let to_ts = |ty: &syn::Type| self.type_to_ts(ty);
let name = ts.ident.to_string();
match name.as_ref() {
"u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "i8" | "i16" | "i32" | "i64"
| "i128" | "isize" | "f64" | "f32" => quote! { number },
"String" | "str" | "char" | "Path" | "PathBuf" => quote! { string },
"bool" => quote! { boolean },
"Box" | "Cow" | "Rc" | "Arc" | "Cell" | "RefCell" if ts.args.len() == 1 => {
to_ts(&ts.args[0])
}
"Duration" => quote! ({ secs: number, nanos: number }),
"SystemTime" => quote! ({
secs_since_epoch: number,
nanos_since_epoch: number
}),
"Vec" | "VecDeque" | "LinkedList" if ts.args.len() == 1 => {
self.type_to_array(&ts.args[0])
}
"HashMap" | "BTreeMap" if ts.args.len() == 2 => {
let k = to_ts(&ts.args[0]);
let v = to_ts(&ts.args[1]);
quote!( { [key: #k]:#v } )
}
"HashSet" | "BTreeSet" if ts.args.len() == 1 => {
let k = to_ts(&ts.args[0]);
quote! ( #k[] )
}
"Option" if ts.args.len() == 1 => {
let k = to_ts(&ts.args[0]);
quote!( #k | null )
}
"Result" if ts.args.len() == 2 => {
let k = to_ts(&ts.args[0]);
let v = to_ts(&ts.args[1]);
quote!( { Ok : #k } | { Err : #v } )
}
"Either" if ts.args.len() == 2 => {
let k = to_ts(&ts.args[0]);
let v = to_ts(&ts.args[1]);
quote!( { Left : #k } | { Right : #v } )
}
"Fn" | "FnOnce" | "FnMut" => {
let args = self.derive_syn_types(&ts.args);
if let Some(ref rt) = ts.return_type {
let rt = to_ts(rt);
quote! { (#(#args),*) => #rt }
} else {
quote! { (#(#args),*) => undefined }
}
}
_ => {
let owned = ts.path();
let path: Vec<&str> = owned.iter().map(|s| s.as_ref()).collect();
match path[..] {
["chrono", "DateTime"] => quote!(string),
_ => {
let ident = &ts.ident;
if !ts.args.is_empty() {
let args = self.derive_syn_types(&ts.args);
quote! { #ident<#(#args),*> }
} else {
quote! {#ident}
}
}
}
}
}
}
fn type_to_array(&self, elem: &syn::Type) -> QuoteT {
if let Some(ty) = self.get_path(elem) {
if ty.ident == "u8" && is_bytes(&self.field) {
return quote!(string);
};
};
let tp = self.type_to_ts(elem);
quote! { #tp[] }
}
pub fn type_to_ts(&self, ty: &syn::Type) -> QuoteT {
use syn::Type::*;
use syn::{
BareFnArgName, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, TypeParamBound,
TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject, TypeTuple,
};
match ty {
Slice(TypeSlice { elem, .. })
| Array(TypeArray { elem, .. })
| Ptr(TypePtr { elem, .. }) => self.type_to_array(elem),
Reference(TypeReference { elem, .. }) => self.type_to_ts(elem),
BareFn(TypeBareFn { output, inputs, .. }) => {
let mut args: Vec<Ident> = Vec::with_capacity(inputs.len());
let mut typs: Vec<&syn::Type> = Vec::with_capacity(inputs.len());
for (idx, t) in inputs.iter().enumerate() {
let i = match t.name {
Some((ref n, _)) => match n {
BareFnArgName::Named(m) => m.clone(),
_ => ident_from_str("_"), },
_ => ident_from_str(&format!("_dummy{}", idx)),
};
args.push(i);
typs.push(&t.ty); }
let typs = self.derive_syn_types_ptr(&typs);
if let Some(ref rt) = return_type(&output) {
let rt = self.type_to_ts(rt);
quote! { ( #(#args: #typs),* ) => #rt }
} else {
quote! { ( #(#args: #typs),* ) => undefined}
}
}
Never(..) => quote! { never },
Tuple(TypeTuple { elems, .. }) => {
let elems = elems.iter().map(|t| self.type_to_ts(t));
quote!([ #(#elems),* ])
}
Path(TypePath { path, .. }) => match last_path_element(&path) {
Some(ref ts) => self.generic_to_ts(ts),
_ => quote! { any },
},
TraitObject(TypeTraitObject { bounds, .. })
| ImplTrait(TypeImplTrait { bounds, .. }) => {
let elems = bounds
.iter()
.filter_map(|t| match t {
TypeParamBound::Trait(t) => last_path_element(&t.path),
_ => None, })
.map(|t| self.generic_to_ts(&t));
quote!(#(#elems)&*)
}
Paren(TypeParen { elem, .. }) | Group(TypeGroup { elem, .. }) => {
let tp = self.type_to_ts(elem);
quote! { ( #tp ) }
}
Infer(..) | Macro(..) | Verbatim(..) => quote! { any },
}
}
pub fn derive_syn_types_ptr(
&'a self,
types: &'a [&'a syn::Type],
) -> impl Iterator<Item = QuoteT> + 'a {
types.iter().map(move |ty| self.type_to_ts(ty))
}
pub fn derive_syn_types(&'a self, types: &'a [syn::Type]) -> impl Iterator<Item = QuoteT> + 'a {
types.iter().map(move |ty| self.type_to_ts(ty))
}
}