AppFlowy/shared-lib/flowy-ast/src/ty_ext.rs
2022-01-23 12:14:00 +08:00

153 lines
4.4 KiB
Rust

use crate::Ctxt;
use syn::{self, AngleBracketedGenericArguments, PathSegment};
#[derive(Eq, PartialEq, Debug)]
pub enum PrimitiveTy {
Map(MapInfo),
Vec,
Opt,
Other,
}
#[derive(Debug)]
pub struct TyInfo<'a> {
pub ident: &'a syn::Ident,
pub ty: &'a syn::Type,
pub primitive_ty: PrimitiveTy,
pub bracket_ty_info: Box<Option<TyInfo<'a>>>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct MapInfo {
pub key: String,
pub value: String,
}
impl MapInfo {
fn new(key: String, value: String) -> Self {
MapInfo { key, value }
}
}
impl<'a> TyInfo<'a> {
#[allow(dead_code)]
pub fn bracketed_ident(&'a self) -> &'a syn::Ident {
match self.bracket_ty_info.as_ref() {
Some(b_ty) => b_ty.ident,
None => {
panic!()
}
}
}
}
pub fn parse_ty<'a>(ctxt: &Ctxt, ty: &'a syn::Type) -> Result<Option<TyInfo<'a>>, String> {
// Type -> TypePath -> Path -> PathSegment -> PathArguments ->
// AngleBracketedGenericArguments -> GenericArgument -> Type.
if let syn::Type::Path(ref p) = ty {
if p.path.segments.len() != 1 {
return Ok(None);
}
let seg = match p.path.segments.last() {
Some(seg) => seg,
None => return Ok(None),
};
let _is_option = seg.ident == "Option";
return if let syn::PathArguments::AngleBracketed(ref bracketed) = seg.arguments {
match seg.ident.to_string().as_ref() {
"HashMap" => generate_hashmap_ty_info(ctxt, ty, seg, bracketed),
"Vec" => generate_vec_ty_info(ctxt, seg, bracketed),
"Option" => generate_option_ty_info(ctxt, ty, seg, bracketed),
_ => {
return Err(format!("Unsupported ty {}", seg.ident));
}
}
} else {
return Ok(Some(TyInfo {
ident: &seg.ident,
ty,
primitive_ty: PrimitiveTy::Other,
bracket_ty_info: Box::new(None),
}));
};
}
ctxt.error_spanned_by(ty, "Unsupported inner type, get inner type fail".to_string());
Ok(None)
}
fn parse_bracketed(bracketed: &AngleBracketedGenericArguments) -> Vec<&syn::Type> {
bracketed
.args
.iter()
.flat_map(|arg| {
if let syn::GenericArgument::Type(ref ty_in_bracket) = arg {
Some(ty_in_bracket)
} else {
None
}
})
.collect::<Vec<&syn::Type>>()
}
pub fn generate_hashmap_ty_info<'a>(
ctxt: &Ctxt,
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Result<Option<TyInfo<'a>>, String> {
// The args of map must greater than 2
if bracketed.args.len() != 2 {
return Ok(None);
}
let types = parse_bracketed(bracketed);
let key = parse_ty(ctxt, types[0])?.unwrap().ident.to_string();
let value = parse_ty(ctxt, types[1])?.unwrap().ident.to_string();
let bracket_ty_info = Box::new(parse_ty(ctxt, types[1])?);
Ok(Some(TyInfo {
ident: &path_segment.ident,
ty,
primitive_ty: PrimitiveTy::Map(MapInfo::new(key, value)),
bracket_ty_info,
}))
}
fn generate_option_ty_info<'a>(
ctxt: &Ctxt,
ty: &'a syn::Type,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Result<Option<TyInfo<'a>>, String> {
assert_eq!(path_segment.ident.to_string(), "Option".to_string());
let types = parse_bracketed(bracketed);
let bracket_ty_info = Box::new(parse_ty(ctxt, types[0])?);
Ok(Some(TyInfo {
ident: &path_segment.ident,
ty,
primitive_ty: PrimitiveTy::Opt,
bracket_ty_info,
}))
}
fn generate_vec_ty_info<'a>(
ctxt: &Ctxt,
path_segment: &'a PathSegment,
bracketed: &'a AngleBracketedGenericArguments,
) -> Result<Option<TyInfo<'a>>, String> {
if bracketed.args.len() != 1 {
return Ok(None);
}
if let syn::GenericArgument::Type(ref bracketed_type) = bracketed.args.first().unwrap() {
let bracketed_ty_info = Box::new(parse_ty(ctxt, bracketed_type)?);
return Ok(Some(TyInfo {
ident: &path_segment.ident,
ty: bracketed_type,
primitive_ty: PrimitiveTy::Vec,
bracket_ty_info: bracketed_ty_info,
}));
}
Ok(None)
}