use {
crate::{
analysis::{
dispatch::DispatchTraitInfo,
get_all_parameters,
},
core::{
config::Config,
constants::attributes::{
ALLOW_NAMED_GENERICS,
DOCUMENT_SIGNATURE,
DOCUMENT_TYPE_PARAMETERS,
DOCUMENT_USE,
},
error_handling::{
CollectErrors,
ErrorCollector,
},
},
documentation::document_signature::generate_signature,
resolution::{
ImplKey,
resolver::{
SelfSubstitutor,
get_concrete_type_name,
get_self_type_info,
merge_generics,
},
},
support::{
attributes::{
AttributeExt,
count_attributes,
find_attribute,
},
documentation_parameters::{
DocumentationParameter,
DocumentationParameters,
},
generate_documentation::format_parameter_doc,
parsing::{
self,
parse_parameter_documentation_pairs,
},
},
},
quote::quote,
syn::{
FnArg,
ImplItem,
Item,
Result,
TraitItem,
Type,
TypeParamBound,
parse_quote,
spanned::Spanned,
visit_mut::VisitMut,
},
};
fn insert_signature_docs(
attrs: &mut Vec<syn::Attribute>,
attr_pos: usize,
sig: &syn::Signature,
config: &Config,
) {
let signature_data = generate_signature(sig, config);
let doc_comment = format!("`{signature_data}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
attrs.insert(attr_pos, header_attr);
}
#[expect(clippy::too_many_arguments, reason = "Documentation generation requires many parameters")]
pub(super) fn process_document_signature(
method: &mut syn::ImplItemFn,
attr_pos: usize,
self_ty: &syn::Type,
self_ty_path: &str,
_trait_name: Option<&str>,
trait_path_str: Option<&str>,
document_use: Option<&str>,
item_impl_generics: &syn::Generics,
config: &Config,
errors: &mut ErrorCollector,
) {
method.attrs.remove(attr_pos);
let mut synthetic_sig = method.sig.clone();
let (base_type_name, impl_generic_params) = get_self_type_info(self_ty, item_impl_generics);
let mut substitutor = SelfSubstitutor::new(
self_ty,
self_ty_path,
trait_path_str,
document_use,
config,
base_type_name.clone(),
impl_generic_params.clone(),
);
substitutor.visit_signature_mut(&mut synthetic_sig);
errors.extend(substitutor.errors);
merge_generics(&mut synthetic_sig, item_impl_generics);
let mut sig_config = config.clone();
if let Some(concrete_type_name) = get_concrete_type_name(self_ty, config) {
sig_config.concrete_types.insert(concrete_type_name.clone());
sig_config.self_type_name = Some(concrete_type_name);
}
insert_signature_docs(&mut method.attrs, attr_pos, &synthetic_sig, &sig_config);
}
fn process_type_parameters_core(
attrs: &mut Vec<syn::Attribute>,
generics: &syn::Generics,
item_label: &str,
attr_pos: usize,
errors: &mut ErrorCollector,
) {
let attr = attrs.remove(attr_pos);
let param_names: Vec<String> = get_all_parameters(generics);
if errors
.collect_our_result(|| {
parsing::parse_has_documentable_items(
param_names.len(),
attr.span(),
DOCUMENT_TYPE_PARAMETERS,
&format!("{item_label} with no type parameters"),
)
})
.is_none()
{
return;
}
if let Some(args) = errors.collect(|| attr.parse_args::<DocumentationParameters>()) {
let entries: Vec<_> = args.entries.into_iter().collect();
if let Some(pairs) = errors.collect_our_result(|| {
parse_parameter_documentation_pairs(param_names, entries, attr.span())
}) {
let mut docs = Vec::new();
docs.push((
String::new(),
r#"### Type Parameters
"#
.to_string(),
));
for (name_from_target, entry) in pairs {
let (name, desc) = match entry {
DocumentationParameter::Override(n, d) => (n.value(), d.value()),
DocumentationParameter::Description(d) => (name_from_target, d.value()),
};
docs.push((name, desc));
}
for (i, (name, desc)) in docs.into_iter().enumerate() {
let doc_comment = format_parameter_doc(&name, &desc);
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
attrs.insert(attr_pos + i, doc_attr);
}
}
} else {
errors.push(syn::Error::new(
attr.span(),
format!("Failed to parse {DOCUMENT_TYPE_PARAMETERS} arguments"),
));
}
}
pub(super) fn process_document_type_parameters(
method: &mut syn::ImplItemFn,
attr_pos: usize,
errors: &mut ErrorCollector,
) {
process_type_parameters_core(
&mut method.attrs,
&method.sig.generics,
&format!("method '{}'", method.sig.ident),
attr_pos,
errors,
);
}
#[expect(clippy::too_many_arguments, reason = "Documentation generation requires many parameters")]
fn process_method_documentation(
method: &mut syn::ImplItemFn,
self_ty: &syn::Type,
self_ty_path: &str,
trait_name: Option<&str>,
trait_path_str: Option<&str>,
impl_document_use: Option<&str>,
item_impl_generics: &syn::Generics,
config: &Config,
errors: &mut ErrorCollector,
) {
method.attrs.retain(|attr| !attr.path().is_ident(ALLOW_NAMED_GENERICS));
let method_document_use = method.attrs.find_value_or_collect(DOCUMENT_USE, errors);
let document_use = method_document_use.or_else(|| impl_document_use.map(String::from));
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_SIGNATURE) {
if count_attributes(&method.attrs, DOCUMENT_SIGNATURE) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_SIGNATURE}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_document_signature(
method,
attr_pos,
self_ty,
self_ty_path,
trait_name,
trait_path_str,
document_use.as_deref(),
item_impl_generics,
config,
errors,
);
}
}
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&method.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_document_type_parameters(method, attr_pos, errors);
}
}
}
fn process_impl_block(
item_impl: &mut syn::ItemImpl,
config: &Config,
errors: &mut ErrorCollector,
) {
let self_ty = &*item_impl.self_ty;
let self_ty_path = quote!(#self_ty).to_string();
let trait_path = item_impl.trait_.as_ref().map(|(_, path, _)| path);
let trait_name = trait_path.and_then(|p| p.segments.last().map(|s| s.ident.to_string()));
let trait_path_str = trait_path.map(|p| quote!(#p).to_string());
if count_attributes(&item_impl.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
item_impl.self_ty.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on impl block for `{self_ty_path}`",
),
));
} else if let Some(attr_pos) = find_attribute(&item_impl.attrs, DOCUMENT_TYPE_PARAMETERS) {
let impl_key = ImplKey::from_paths(&self_ty_path, trait_path_str.as_deref());
if let Some(impl_docs) = config.impl_type_param_docs.get(&impl_key) {
item_impl.attrs.remove(attr_pos);
let mut docs = Vec::new();
docs.push((
String::new(),
r#"### Type Parameters
"#
.to_string(),
));
for (param_name, desc) in impl_docs.iter() {
docs.push((param_name.clone(), desc.clone()));
}
for (i, (name, desc)) in docs.into_iter().enumerate() {
let doc_comment = format_parameter_doc(&name, &desc);
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_impl.attrs.insert(attr_pos + i, doc_attr);
}
} else {
item_impl.attrs.remove(attr_pos);
}
}
let impl_document_use = item_impl.attrs.find_value_or_collect(DOCUMENT_USE, errors);
for impl_item in &mut item_impl.items {
if let ImplItem::Fn(method) = impl_item {
process_method_documentation(
method,
self_ty,
&self_ty_path,
trait_name.as_deref(),
trait_path_str.as_deref(),
impl_document_use.as_deref(),
&item_impl.generics,
config,
errors,
);
}
}
}
fn process_trait_method_documentation(
method: &mut syn::TraitItemFn,
config: &Config,
errors: &mut ErrorCollector,
) {
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_SIGNATURE) {
if count_attributes(&method.attrs, DOCUMENT_SIGNATURE) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_SIGNATURE}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
method.attrs.remove(attr_pos);
insert_signature_docs(&mut method.attrs, attr_pos, &method.sig, config);
}
}
if let Some(attr_pos) = find_attribute(&method.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&method.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
method.sig.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on method `{}`",
method.sig.ident
),
));
} else {
process_type_parameters_core(
&mut method.attrs,
&method.sig.generics,
&format!("method '{}'", method.sig.ident),
attr_pos,
errors,
);
}
}
}
fn process_trait_block(
item_trait: &mut syn::ItemTrait,
config: &Config,
errors: &mut ErrorCollector,
) {
if let Some(attr_pos) = find_attribute(&item_trait.attrs, DOCUMENT_TYPE_PARAMETERS) {
if count_attributes(&item_trait.attrs, DOCUMENT_TYPE_PARAMETERS) > 1 {
errors.push(syn::Error::new(
item_trait.ident.span(),
format!(
"#[{DOCUMENT_TYPE_PARAMETERS}] can only be used once per item. Remove the duplicate attribute on trait `{}`",
item_trait.ident
),
));
} else {
process_type_parameters_core(
&mut item_trait.attrs,
&item_trait.generics,
&format!("trait '{}'", item_trait.ident),
attr_pos,
errors,
);
}
}
for item in &mut item_trait.items {
if let TraitItem::Fn(method) = item {
process_trait_method_documentation(method, config, errors);
}
}
}
pub(super) fn generate_documentation(
items: &mut [Item],
config: &Config,
) -> Result<()> {
let mut errors = ErrorCollector::new();
for item in items {
match item {
Item::Impl(item_impl) => process_impl_block(item_impl, config, &mut errors),
Item::Trait(item_trait) => process_trait_block(item_trait, config, &mut errors),
Item::Fn(item_fn) => {
item_fn.attrs.retain(|attr| !attr.path().is_ident(ALLOW_NAMED_GENERICS));
process_fn_dispatch_signature(item_fn, config);
}
_ => {}
}
}
errors.finish()
}
fn process_fn_dispatch_signature(
item_fn: &mut syn::ItemFn,
config: &Config,
) {
let Some(attr_pos) = find_attribute(&item_fn.attrs, DOCUMENT_SIGNATURE) else {
return;
};
if let Some(attr) = item_fn.attrs.get(attr_pos)
&& let Some(manual_sig) = extract_manual_signature(attr)
{
item_fn.attrs.remove(attr_pos);
let doc_comment = format!("`{manual_sig}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_fn.attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
item_fn.attrs.insert(attr_pos, header_attr);
return;
}
let Some(dispatch_info) = find_dispatch_trait_in_sig(&item_fn.sig, config) else {
return;
};
let Some(synthetic_sig) = build_synthetic_signature(&item_fn.sig, &dispatch_info) else {
return;
};
item_fn.attrs.remove(attr_pos);
let sig_data = generate_signature(&synthetic_sig, config);
let doc_comment = format!("`{sig_data}`");
let doc_attr: syn::Attribute = parse_quote!(#[doc = #doc_comment]);
item_fn.attrs.insert(attr_pos, doc_attr);
let header_attr: syn::Attribute = parse_quote!(#[doc = r#"### Type Signature
"#]);
item_fn.attrs.insert(attr_pos, header_attr);
}
fn extract_manual_signature(attr: &syn::Attribute) -> Option<String> {
let syn::Meta::List(meta_list) = &attr.meta else {
return None;
};
let lit: syn::LitStr = syn::parse2(meta_list.tokens.clone()).ok()?;
let value = lit.value();
if value.is_empty() { None } else { Some(value) }
}
fn find_dispatch_trait_in_sig(
sig: &syn::Signature,
config: &Config,
) -> Option<DispatchTraitInfo> {
for input in &sig.inputs {
let FnArg::Typed(pat_type) = input else {
continue;
};
let Type::ImplTrait(impl_trait) = &*pat_type.ty else {
continue;
};
for bound in &impl_trait.bounds {
let TypeParamBound::Trait(trait_bound) = bound else {
continue;
};
let Some(segment) = trait_bound.path.segments.last() else {
continue;
};
let name = segment.ident.to_string();
if let Some(info) = config.dispatch_traits.get(&name) {
return Some(info.clone());
}
}
}
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate {
for bound in &pred_type.bounds {
if let TypeParamBound::Trait(trait_bound) = bound {
let Some(segment) = trait_bound.path.segments.last() else {
continue;
};
let name = segment.ident.to_string();
if let Some(info) = config.dispatch_traits.get(&name) {
return Some(info.clone());
}
}
}
}
}
}
None
}
fn build_synthetic_signature(
_original_sig: &syn::Signature,
dispatch_info: &DispatchTraitInfo,
) -> Option<syn::Signature> {
let kind_trait_name = dispatch_info.kind_trait_name.as_ref()?;
let brand_param = &dispatch_info.brand_param;
let kind_ident: syn::Ident = syn::parse_str(kind_trait_name).ok()?;
let brand_ident: syn::Ident = syn::parse_str(brand_param).ok()?;
let mut generic_params: Vec<syn::GenericParam> = Vec::new();
generic_params.push(parse_quote!('a));
if let Some(ref constraint_name) = dispatch_info.semantic_constraint {
let constraint_ident: syn::Ident = syn::parse_str(constraint_name).ok()?;
generic_params.push(parse_quote!(#brand_ident: #constraint_ident + #kind_ident));
} else {
generic_params.push(parse_quote!(#brand_ident: #kind_ident));
}
let mut all_element_types: Vec<String> = Vec::new();
let secondary_map: std::collections::HashMap<&str, &str> =
dispatch_info.secondary_constraints.iter().map(|(p, c)| (p.as_str(), c.as_str())).collect();
for param_name in &dispatch_info.type_param_order {
if param_name == brand_param {
continue;
}
if let Some(constraint_name) = secondary_map.get(param_name.as_str()) {
let param_ident: syn::Ident = syn::parse_str(param_name).ok()?;
let constraint_ident: syn::Ident = syn::parse_str(constraint_name).ok()?;
generic_params.push(parse_quote!(#param_ident: #constraint_ident + #kind_ident));
continue;
}
if let Ok(param_ident) = syn::parse_str::<syn::Ident>(param_name) {
generic_params.push(parse_quote!(#param_ident: 'a));
all_element_types.push(param_name.clone());
}
}
let container_map = build_container_map(_original_sig, dispatch_info);
let mut fn_params: Vec<syn::FnArg> = Vec::new();
for input in &_original_sig.inputs {
let FnArg::Typed(pat_type) = input else {
continue;
};
if matches!(&*pat_type.ty, Type::ImplTrait(_))
&& let Some(ref arrow) = dispatch_info.arrow_type
&& let Some(closure_param) =
build_closure_param(arrow, dispatch_info.tuple_closure, &brand_ident, &kind_ident)
{
fn_params.push(closure_param);
continue;
}
if matches!(&*pat_type.ty, Type::ImplTrait(_)) {
continue;
}
if dispatch_info.tuple_closure
&& matches!(&*pat_type.ty, Type::Tuple(tuple) if tuple.elems.len() >= 2)
&& let Some(ref arrow) = dispatch_info.arrow_type
&& let Some(closure_param) = build_closure_param(arrow, true, &brand_ident, &kind_ident)
{
fn_params.push(closure_param);
continue;
}
let ty = &pat_type.ty;
let type_str = quote!(#ty).to_string().replace(' ', "");
if let Some(elements) = container_map.get(&type_str) {
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, elements)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
if let Type::Path(type_path) = &*pat_type.ty
&& type_path.qself.is_some()
&& let Some(last_seg) = type_path.path.segments.last()
{
let assoc_name = last_seg.ident.to_string();
if let Some((_, elements)) =
dispatch_info.associated_types.iter().find(|(name, _)| name == &assoc_name)
{
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, elements)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
}
if is_inferable_brand_param(&type_str, _original_sig) {
use crate::analysis::dispatch::ReturnStructure;
let element_types: Option<Vec<String>> = dispatch_info
.self_type_elements
.clone()
.or_else(|| {
let elems: Vec<String> = dispatch_info
.type_param_order
.iter()
.filter(|p| {
*p != brand_param
&& p.len() == 1 && !dispatch_info
.secondary_constraints
.iter()
.any(|(sc, _)| sc == *p)
})
.cloned()
.collect();
if elems.is_empty() { None } else { Some(elems) }
})
.or_else(|| match &dispatch_info.return_structure {
ReturnStructure::Applied(args) => Some(args.clone()),
ReturnStructure::Nested {
inner_args, ..
} => Some(inner_args.clone()),
ReturnStructure::Tuple(elements) => elements.first().cloned(),
ReturnStructure::NestedTuple {
inner_elements, ..
} => inner_elements.first().cloned(),
ReturnStructure::Plain(_) => None,
});
if let Some(ref elems) = element_types {
let pat = &pat_type.pat;
let container_type = build_applied_type(&brand_ident, &kind_ident, elems)?;
fn_params.push(parse_quote!(#pat: #container_type));
continue;
}
}
fn_params.push(input.clone());
}
let return_type =
build_return_type(&dispatch_info.return_structure, &brand_ident, &kind_ident)?;
let generics = syn::Generics {
lt_token: Some(Default::default()),
params: generic_params.into_iter().collect(),
gt_token: Some(Default::default()),
where_clause: None,
};
Some(syn::Signature {
constness: None,
asyncness: None,
unsafety: None,
abi: None,
fn_token: Default::default(),
ident: syn::parse_str("synthetic").ok()?,
generics,
paren_token: Default::default(),
inputs: fn_params.into_iter().collect(),
variadic: None,
output: syn::ReturnType::Type(Default::default(), Box::new(return_type)),
})
}
fn build_container_map(
sig: &syn::Signature,
dispatch_info: &DispatchTraitInfo,
) -> std::collections::HashMap<String, Vec<String>> {
if dispatch_info.container_params.is_empty() {
return std::collections::HashMap::new();
}
let fn_type_args = extract_dispatch_type_args(sig);
if fn_type_args.is_empty() {
return std::collections::HashMap::new();
}
let mut result = std::collections::HashMap::new();
for cp in &dispatch_info.container_params {
if let Some(fn_arg) = fn_type_args.get(cp.position) {
result.insert(fn_arg.clone(), cp.element_types.clone());
}
}
result
}
fn extract_dispatch_type_args(sig: &syn::Signature) -> Vec<String> {
for input in &sig.inputs {
let FnArg::Typed(pat_type) = input else { continue };
let Type::ImplTrait(impl_trait) = &*pat_type.ty else { continue };
for bound in &impl_trait.bounds {
if let Some(args) = extract_dispatch_trait_args(bound) {
return args;
}
}
}
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate {
for bound in &pred_type.bounds {
if let Some(args) = extract_dispatch_trait_args(bound) {
return args;
}
}
}
}
}
Vec::new()
}
fn extract_dispatch_trait_args(bound: &TypeParamBound) -> Option<Vec<String>> {
let TypeParamBound::Trait(trait_bound) = bound else {
return None;
};
let segment = trait_bound.path.segments.last()?;
if !segment.ident.to_string().ends_with(crate::core::constants::markers::DISPATCH_SUFFIX) {
return None;
}
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
return None;
};
Some(
args.args
.iter()
.filter_map(|arg| {
if let syn::GenericArgument::Type(ty) = arg {
Some(quote!(#ty).to_string().replace(' ', ""))
} else {
None
}
})
.collect(),
)
}
fn is_inferable_brand_param(
type_name: &str,
sig: &syn::Signature,
) -> bool {
if let Some(where_clause) = &sig.generics.where_clause {
for predicate in &where_clause.predicates {
if let syn::WherePredicate::Type(pred_type) = predicate {
let bounded_ty = &pred_type.bounded_ty;
let param_name = quote!(#bounded_ty).to_string().replace(' ', "");
if param_name == type_name {
for bound in &pred_type.bounds {
if let TypeParamBound::Trait(trait_bound) = bound {
let name = trait_bound
.path
.segments
.last()
.map(|s| s.ident.to_string())
.unwrap_or_default();
if name.starts_with("InferableBrand_") {
return true;
}
}
}
}
}
}
}
false
}
fn build_applied_type(
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
element_types: &[String],
) -> Option<syn::Type> {
let mut args = vec![quote!('a)];
for elem in element_types {
let elem_type: syn::Type = syn::parse_str(elem).ok()?;
args.push(quote!(#elem_type));
}
let args_tokens = quote!(#(#args),*);
Some(parse_quote!(<#brand_ident as #kind_ident>::Of<#args_tokens>))
}
fn build_closure_param(
arrow: &crate::analysis::dispatch::DispatchArrow,
tuple_closure: bool,
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
) -> Option<syn::FnArg> {
use crate::analysis::dispatch::{
ArrowOutput,
DispatchArrowParam,
};
if tuple_closure {
let mut fn_types: Vec<syn::Type> = Vec::new();
for param in &arrow.inputs {
let sub_arrow = match param {
DispatchArrowParam::SubArrow(arrow) => arrow,
DispatchArrowParam::TypeParam(sub_arrow_str) => {
if let Some(arrow_pos) = sub_arrow_str.rfind(" -> ") {
let input_str = &sub_arrow_str[.. arrow_pos];
let output_str = &sub_arrow_str[arrow_pos + 4 ..];
let input_str =
input_str.trim().trim_start_matches('(').trim_end_matches(')');
let input_types: Vec<syn::Type> = input_str
.split(',')
.filter_map(|s| syn::parse_str(s.trim()).ok())
.collect();
let output_type: syn::Type =
syn::parse_str(output_str.trim()).unwrap_or_else(|_| parse_quote!(()));
fn_types.push(parse_quote!(fn(#(#input_types),*) -> #output_type));
}
continue;
}
_ => continue,
};
let mut input_types: Vec<syn::Type> = Vec::new();
for sub_param in &sub_arrow.inputs {
match sub_param {
DispatchArrowParam::TypeParam(name) => {
let ident: syn::Ident = syn::parse_str(name).ok()?;
input_types.push(parse_quote!(#ident));
}
DispatchArrowParam::AssociatedType {
assoc_name,
} => {
let assoc_ident: syn::Ident = syn::parse_str(assoc_name).ok()?;
input_types.push(parse_quote!(#brand_ident::#assoc_ident));
}
DispatchArrowParam::SubArrow(_) => continue,
}
}
let output_type: syn::Type = match &sub_arrow.output {
ArrowOutput::Plain(s) => syn::parse_str(s).ok()?,
ArrowOutput::BrandApplied(args) =>
build_applied_type(brand_ident, kind_ident, args)?,
ArrowOutput::OtherApplied {
brand,
args,
} => {
let other_ident: syn::Ident = syn::parse_str(brand).ok()?;
build_applied_type(&other_ident, kind_ident, args)?
}
};
fn_types.push(parse_quote!(fn(#(#input_types),*) -> #output_type));
}
if fn_types.is_empty() {
return None;
}
return Some(parse_quote!(fg: (#(#fn_types),*)));
}
let mut input_types: Vec<syn::Type> = Vec::new();
for param in &arrow.inputs {
match param {
DispatchArrowParam::TypeParam(name) => {
let ident: syn::Ident = syn::parse_str(name).ok()?;
input_types.push(parse_quote!(#ident));
}
DispatchArrowParam::AssociatedType {
assoc_name,
} => {
let assoc_ident: syn::Ident = syn::parse_str(assoc_name).ok()?;
input_types.push(parse_quote!(#brand_ident::#assoc_ident));
}
DispatchArrowParam::SubArrow(_) => {
continue;
}
}
}
let output_type: syn::Type = match &arrow.output {
ArrowOutput::Plain(s) => syn::parse_str(s).ok()?,
ArrowOutput::BrandApplied(args) => build_applied_type(brand_ident, kind_ident, args)?,
ArrowOutput::OtherApplied {
brand,
args,
} => {
let other_ident: syn::Ident = syn::parse_str(brand).ok()?;
build_applied_type(&other_ident, kind_ident, args)?
}
};
Some(parse_quote!(f: impl Fn(#(#input_types),*) -> #output_type + 'a))
}
fn build_return_type(
ret: &crate::analysis::dispatch::ReturnStructure,
brand_ident: &syn::Ident,
kind_ident: &syn::Ident,
) -> Option<syn::Type> {
use crate::analysis::dispatch::ReturnStructure;
match ret {
ReturnStructure::Plain(var) => Some(syn::parse_str(var).ok()?),
ReturnStructure::Applied(args) => build_applied_type(brand_ident, kind_ident, args),
ReturnStructure::Nested {
outer_param,
inner_args,
} => {
let outer_ident: syn::Ident = syn::parse_str(outer_param).ok()?;
let inner_type = build_applied_type(brand_ident, kind_ident, inner_args)?;
Some(parse_quote!(<#outer_ident as #kind_ident>::Of<'a, #inner_type>))
}
ReturnStructure::Tuple(elements) => {
let elem_types: Vec<syn::Type> = elements
.iter()
.filter_map(|args| build_applied_type(brand_ident, kind_ident, args))
.collect();
Some(parse_quote!((#(#elem_types),*)))
}
ReturnStructure::NestedTuple {
outer_param,
inner_elements,
} => {
let outer_ident: syn::Ident = syn::parse_str(outer_param).ok()?;
let tuple_types: Vec<syn::Type> = inner_elements
.iter()
.filter_map(|args| build_applied_type(brand_ident, kind_ident, args))
.collect();
let tuple_type: syn::Type = parse_quote!((#(#tuple_types),*));
Some(parse_quote!(<#outer_ident as #kind_ident>::Of<'a, #tuple_type>))
}
}
}