use std::collections::HashMap;
use syn::{ItemFn, Meta};
use crate::{Argument, Function, TypeRecipe};
pub mod arguments;
pub mod docstring;
pub mod signature;
pub mod partial;
use darling::{Error, Result, ast::NestedMeta};
use crate::bootstrap::{arguments::BootstrapArguments, docstring::BootstrapDocstring};
use self::{
arguments::{BootType, DerivedTypes},
signature::{BootSigArgType, BootstrapSignature},
};
#[cfg(test)]
mod test;
impl Function {
pub fn from_ast(
attr_args: Vec<Meta>,
item_fn: ItemFn,
module: Option<&str>,
) -> Result<Function> {
let arguments = BootstrapArguments::from_attribute_args(
&attr_args
.into_iter()
.map(NestedMeta::Meta)
.collect::<Vec<_>>(),
)?;
let signature = BootstrapSignature::from_syn(item_fn.sig.clone())?;
let name = arguments.name.clone().unwrap_or(signature.name.clone());
let rust_path = if let Some(path) = &arguments.rust_path {
Some(path.clone())
} else if arguments.name.is_none() {
module.map(|module| format!("{module}/fn.{name}"))
} else {
None
};
let docstring = BootstrapDocstring::from_attrs(
&name,
item_fn.attrs,
&item_fn.sig.output,
rust_path,
arguments.features.0.clone(),
)?;
reconcile_function(arguments, docstring, signature)
}
}
pub fn reconcile_function(
mut bootstrap: BootstrapArguments,
mut doc_comments: BootstrapDocstring,
signature: BootstrapSignature,
) -> Result<Function> {
Ok(Function {
name: bootstrap.name.unwrap_or(signature.name),
features: bootstrap.features.0,
description: doc_comments.description,
args: reconcile_arguments(
&mut bootstrap.arguments.0,
&mut doc_comments.arguments,
signature.arguments,
signature.supports_partial,
)?
.into_iter()
.chain(reconcile_generics(
&mut bootstrap.generics.0,
&mut doc_comments.generics,
signature.generics,
)?)
.collect(),
ret: reconcile_return(
bootstrap.returns,
doc_comments.returns,
signature.output_c_type,
)?,
derived_types: reconcile_derived_types(bootstrap.derived_types),
supports_partial: signature.supports_partial,
has_ffi: bootstrap.has_ffi.unwrap_or(true),
deprecation: doc_comments.deprecated,
})
}
fn reconcile_arguments(
bootstrap_args: &mut HashMap<String, BootType>,
doc_comments: &mut HashMap<String, String>,
arguments: Vec<(String, BootSigArgType)>,
supports_partial: bool,
) -> Result<Vec<Argument>> {
(arguments.into_iter().enumerate())
.map(|(i, (name, arg_type))| {
let boot_type = bootstrap_args.remove(&name).unwrap_or_default();
Ok(Argument {
name: Some(name.clone()),
c_type: Some(match boot_type.c_type {
Some(v) => v,
None => {
if supports_partial && i < 2 {
if i == 0 { "AnyDomain *" } else { "AnyMetric *" }.to_string()
} else {
arg_type.c_type?
}
}
}),
rust_type: Some(match boot_type.rust_type {
Some(v) => v,
None => {
if supports_partial && i < 2 {
TypeRecipe::None
} else {
arg_type.rust_type?
}
}
}),
description: doc_comments.remove(&name),
hint: boot_type.hint,
default: boot_type.default,
is_type: false,
do_not_convert: boot_type.do_not_convert,
example: None,
})
})
.collect()
}
fn reconcile_generics(
bootstrap_args: &mut HashMap<String, BootType>,
doc_comments: &mut HashMap<String, String>,
generics: Vec<String>,
) -> Result<Vec<Argument>> {
(generics.into_iter())
.filter_map(|name| {
let boot_type = bootstrap_args.remove(&name).unwrap_or_default();
if boot_type.suppress {
return None;
}
if boot_type.c_type.is_some() {
return Some(Err(Error::custom(
"c_type should not be specified on generics",
)));
}
Some(Ok(Argument {
name: Some(name.clone()),
description: doc_comments.remove(&name),
hint: boot_type.hint,
default: boot_type.default,
is_type: true,
example: boot_type.example,
..Default::default()
}))
})
.collect()
}
fn reconcile_return(
boot_type: Option<BootType>,
doc_comment: Option<String>,
output_c_type: Result<String>,
) -> Result<Argument> {
let boot_type = boot_type.unwrap_or_default();
Ok(Argument {
c_type: Some(match boot_type.c_type {
Some(v) => v,
None => output_c_type?,
}),
rust_type: boot_type.rust_type,
description: doc_comment,
do_not_convert: boot_type.do_not_convert,
hint: boot_type.hint,
..Default::default()
})
}
fn reconcile_derived_types(derived_types: Option<DerivedTypes>) -> Vec<Argument> {
derived_types
.map(|dt| dt.0)
.unwrap_or_default()
.into_iter()
.map(|(name, rt)| Argument {
name: Some(name),
rust_type: Some(rt),
is_type: true,
..Default::default()
})
.collect()
}