mod ty; use std::string::String; use log::debug; use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::*; use crate::ir::*; use crate::generator::rust::HANDLER_NAME; use crate::parser::ty::TypeParser; use crate::source_graph::Crate; const STREAM_SINK_IDENT: &str = "StreamSink"; const RESULT_IDENT: &str = "Result"; pub fn parse(source_rust_content: &str, file: File, manifest_path: &str) -> IrFile { let crate_map = Crate::new(manifest_path); let src_fns = extract_fns_from_file(&file); let src_structs = crate_map.root_module.collect_structs_to_vec(); let src_enums = crate_map.root_module.collect_enums_to_vec(); let parser = Parser::new(TypeParser::new(src_structs, src_enums)); parser.parse(source_rust_content, src_fns) } struct Parser<'a> { type_parser: TypeParser<'a>, } impl<'a> Parser<'a> { pub fn new(type_parser: TypeParser<'a>) -> Self { Parser { type_parser } } } impl<'a> Parser<'a> { fn parse(mut self, source_rust_content: &str, src_fns: Vec<&ItemFn>) -> IrFile { let funcs = src_fns.iter().map(|f| self.parse_function(f)).collect(); let has_executor = source_rust_content.contains(HANDLER_NAME); let (struct_pool, enum_pool) = self.type_parser.consume(); IrFile { funcs, struct_pool, enum_pool, has_executor, } } /// Attempts to parse the type from the return part of a function signature. There is a special /// case for top-level `Result` types. pub fn try_parse_fn_output_type(&mut self, ty: &syn::Type) -> Option { let inner = ty::SupportedInnerType::try_from_syn_type(ty)?; match inner { ty::SupportedInnerType::Path(ty::SupportedPathType { ident, generic: Some(generic), }) if ident == RESULT_IDENT => Some(IrFuncOutput::ResultType( self.type_parser.convert_to_ir_type(*generic)?, )), _ => Some(IrFuncOutput::Type( self.type_parser.convert_to_ir_type(inner)?, )), } } /// Attempts to parse the type from an argument of a function signature. There is a special /// case for top-level `StreamSink` types. pub fn try_parse_fn_arg_type(&mut self, ty: &syn::Type) -> Option { match ty { syn::Type::Path(syn::TypePath { path, .. }) => { let last_segment = path.segments.last().unwrap(); if last_segment.ident == STREAM_SINK_IDENT { match &last_segment.arguments { syn::PathArguments::AngleBracketed( syn::AngleBracketedGenericArguments { args, .. }, ) if args.len() == 1 => { // Unwrap is safe here because args.len() == 1 match args.last().unwrap() { syn::GenericArgument::Type(t) => { Some(IrFuncArg::StreamSinkType(self.type_parser.parse_type(t))) } _ => None, } } _ => None, } } else { Some(IrFuncArg::Type(self.type_parser.parse_type(ty))) } } _ => None, } } fn parse_function(&mut self, func: &ItemFn) -> IrFunc { debug!("parse_function function name: {:?}", func.sig.ident); let sig = &func.sig; let func_name = sig.ident.to_string(); let mut inputs = Vec::new(); let mut output = None; let mut mode = None; let mut fallible = true; for sig_input in &sig.inputs { if let FnArg::Typed(ref pat_type) = sig_input { let name = if let Pat::Ident(ref pat_ident) = *pat_type.pat { format!("{}", pat_ident.ident) } else { panic!("unexpected pat_type={:?}", pat_type) }; match self.try_parse_fn_arg_type(&pat_type.ty).unwrap_or_else(|| { panic!( "Failed to parse function argument type `{}`", type_to_string(&pat_type.ty) ) }) { IrFuncArg::StreamSinkType(ty) => { output = Some(ty); mode = Some(IrFuncMode::Stream); } IrFuncArg::Type(ty) => { inputs.push(IrField { name: IrIdent::new(name), ty, is_final: true, comments: extract_comments(&pat_type.attrs), }); } } } else { panic!("unexpected sig_input={:?}", sig_input); } } if output.is_none() { output = Some(match &sig.output { ReturnType::Type(_, ty) => { match self.try_parse_fn_output_type(ty).unwrap_or_else(|| { panic!( "Failed to parse function output type `{}`", type_to_string(ty) ) }) { IrFuncOutput::ResultType(ty) => ty, IrFuncOutput::Type(ty) => { fallible = false; ty } } } ReturnType::Default => { fallible = false; IrType::Primitive(IrTypePrimitive::Unit) } }); mode = Some( if let Some(IrType::Delegate(IrTypeDelegate::SyncReturnVecU8)) = output { IrFuncMode::Sync } else { IrFuncMode::Normal }, ); } // let comments = func.attrs.iter().filter_map(extract_comments).collect(); IrFunc { name: func_name, inputs, output: output.expect("unsupported output"), fallible, mode: mode.expect("unsupported mode"), comments: extract_comments(&func.attrs), } } } fn extract_fns_from_file(file: &File) -> Vec<&ItemFn> { let mut src_fns = Vec::new(); for item in file.items.iter() { if let Item::Fn(ref item_fn) = item { if let Visibility::Public(_) = &item_fn.vis { src_fns.push(item_fn); } } } src_fns } fn extract_comments(attrs: &[Attribute]) -> Vec { attrs .iter() .filter_map(|attr| match attr.parse_meta() { Ok(Meta::NameValue(MetaNameValue { path, lit: Lit::Str(lit), .. })) if path.is_ident("doc") => Some(IrComment::from(lit.value().as_ref())), _ => None, }) .collect() } pub mod frb_keyword { syn::custom_keyword!(mirror); syn::custom_keyword!(non_final); syn::custom_keyword!(dart_metadata); syn::custom_keyword!(import); } #[derive(Clone, Debug)] pub struct NamedOption { pub name: K, pub value: V, } impl Parse for NamedOption { fn parse(input: ParseStream<'_>) -> Result { let name: K = input.parse()?; let _: Token![=] = input.parse()?; let value = input.parse()?; Ok(Self { name, value }) } } #[derive(Clone, Debug)] pub struct MirrorOption(Path); impl Parse for MirrorOption { fn parse(input: ParseStream<'_>) -> Result { let content; parenthesized!(content in input); let path: Path = content.parse()?; Ok(Self(path)) } } #[derive(Clone, Debug)] pub struct MetadataAnnotations(Vec); impl Parse for IrDartAnnotation { fn parse(input: ParseStream<'_>) -> Result { let annotation: LitStr = input.parse()?; let library = if input.peek(frb_keyword::import) { let _ = input.parse::()?; let library: IrDartImport = input.parse()?; Some(library) } else { None }; Ok(Self { content: annotation.value(), library, }) } } impl Parse for MetadataAnnotations { fn parse(input: ParseStream<'_>) -> Result { let content; parenthesized!(content in input); let annotations = Punctuated::::parse_terminated(&content)? .into_iter() .collect(); Ok(Self(annotations)) } } #[derive(Clone, Debug)] pub struct DartImports(Vec); impl Parse for IrDartImport { fn parse(input: ParseStream<'_>) -> Result { let uri: LitStr = input.parse()?; let alias: Option = if input.peek(token::As) { let _ = input.parse::()?; let alias: Ident = input.parse()?; Some(alias.to_string()) } else { None }; Ok(Self { uri: uri.value(), alias, }) } } impl Parse for DartImports { fn parse(input: ParseStream<'_>) -> Result { let content; parenthesized!(content in input); let imports = Punctuated::::parse_terminated(&content)? .into_iter() .collect(); Ok(Self(imports)) } } enum FrbOption { Mirror(MirrorOption), NonFinal, Metadata(NamedOption), } impl Parse for FrbOption { fn parse(input: ParseStream<'_>) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(frb_keyword::mirror) { input.parse().map(FrbOption::Mirror) } else if lookahead.peek(frb_keyword::non_final) { input .parse::() .map(|_| FrbOption::NonFinal) } else if lookahead.peek(frb_keyword::dart_metadata) { input.parse().map(FrbOption::Metadata) } else { Err(lookahead.error()) } } } fn extract_metadata(attrs: &[Attribute]) -> Vec { attrs .iter() .filter(|attr| attr.path.is_ident("frb")) .map(|attr| attr.parse_args::()) .flat_map(|frb_option| match frb_option { Ok(FrbOption::Metadata(NamedOption { name: _, value: MetadataAnnotations(annotations), })) => annotations, _ => vec![], }) .collect() } /// syn -> string https://github.com/dtolnay/syn/issues/294 fn type_to_string(ty: &Type) -> String { quote!(#ty).to_string().replace(' ', "") }