use {
crate::{
analysis::{
get_apply_macro_parameters,
get_fn_brand_info,
traits::format_brand_name,
},
core::{
config::Config,
constants::{
markers,
types,
},
},
hm::{
HmAst,
converter::{
get_smart_pointer_inner,
is_phantom_data_path,
is_smart_pointer,
trait_bound_to_hm_type,
},
},
support::{
TypeVisitor,
last_path_segment,
},
},
std::collections::{
HashMap,
HashSet,
},
syn::{
GenericArgument,
PathArguments,
ReturnType,
TypeParamBound,
},
};
pub struct HmAstBuilder<'a> {
pub fn_bounds: &'a HashMap<String, HmAst>,
pub generic_names: &'a HashSet<String>,
pub config: &'a Config,
}
impl<'a> TypeVisitor for HmAstBuilder<'a> {
type Output = HmAst;
fn default_output(&self) -> Self::Output {
HmAst::Unit
}
fn visit_path(
&mut self,
type_path: &syn::TypePath,
) -> Self::Output {
if let Some(fn_brand_info) = get_fn_brand_info(type_path, self.config) {
let input_hm_types: Vec<_> =
fn_brand_info.inputs.iter().map(|ty| self.visit(ty)).collect();
let output_hm = self.visit(&fn_brand_info.output);
let input = if input_hm_types.is_empty() {
HmAst::Unit
} else if input_hm_types.len() == 1 {
#[expect(clippy::indexing_slicing, reason = "Length checked above")]
input_hm_types[0].clone()
} else {
HmAst::Tuple(input_hm_types)
};
return HmAst::Arrow(Box::new(input), Box::new(output_hm));
}
if let Some(type_path_inner) = &type_path.qself {
let constructor_type = self.visit(&type_path_inner.ty);
let Some(last_segment) = last_path_segment(&type_path.path) else {
return HmAst::Variable("unknown".to_string());
};
let mut args_list = Vec::new();
if let PathArguments::AngleBracketed(args) = &last_segment.arguments {
for arg in &args.args {
if let GenericArgument::Type(inner_ty) = arg {
args_list.push(self.visit(inner_ty));
}
}
}
match constructor_type {
HmAst::Variable(name) =>
if args_list.is_empty() {
HmAst::Variable(name)
} else {
HmAst::Constructor(name, args_list)
},
HmAst::Constructor(name, mut prev_args) => {
prev_args.extend(args_list);
HmAst::Constructor(name, prev_args)
}
_ => {
let name = format!("{constructor_type}");
HmAst::Constructor(name, args_list)
}
}
} else {
if is_phantom_data_path(type_path) {
return HmAst::Unit;
}
if type_path.path.segments.len() >= 2 {
#[expect(clippy::indexing_slicing, reason = "Length checked above")]
let first = &type_path.path.segments[0];
let Some(last) = last_path_segment(&type_path.path) else {
return HmAst::Variable("unknown".to_string());
};
let first_name = first.ident.to_string();
if type_path.path.segments.len() == 2
&& self.generic_names.contains(&first_name)
&& matches!(last.arguments, PathArguments::None)
{
return HmAst::Variable(last.ident.to_string());
}
let mut constructor_name = first_name;
if self.config.concrete_types.contains(&constructor_name) {
} else if self.generic_names.contains(&constructor_name) {
} else if constructor_name == types::SELF {
constructor_name = self
.config
.self_type_name
.clone()
.unwrap_or_else(|| types::SELF.to_string());
} else {
constructor_name = format_brand_name(&constructor_name, self.config);
}
if let PathArguments::AngleBracketed(args) = &last.arguments {
let mut type_args = Vec::new();
for arg in &args.args {
if let GenericArgument::Type(inner_ty) = arg {
type_args.push(self.visit(inner_ty));
}
}
if !type_args.is_empty() {
return HmAst::Constructor(constructor_name, type_args);
}
}
return HmAst::Variable(constructor_name);
}
let Some(segment) = last_path_segment(&type_path.path) else {
return HmAst::Variable("unknown".to_string());
};
let name = segment.ident.to_string();
if is_smart_pointer(&name)
&& let Some(inner_ty) = get_smart_pointer_inner(segment)
{
return self.visit(inner_ty);
}
if let Some(sig) = self.fn_bounds.get(&name) {
if let HmAst::Variable(v) = sig
&& v == markers::FN_BRAND_MARKER
{
return HmAst::Variable(name);
}
return sig.clone();
}
if self.config.concrete_types.contains(&name) {
match &segment.arguments {
PathArguments::AngleBracketed(args) => {
let mut type_args = Vec::new();
for arg in &args.args {
if let GenericArgument::Type(inner_ty) = arg {
type_args.push(self.visit(inner_ty));
}
}
if type_args.is_empty() {
return HmAst::Variable(name);
} else {
return HmAst::Constructor(name, type_args);
}
}
_ => return HmAst::Variable(name),
}
}
if self.generic_names.contains(&name) {
return HmAst::Variable(name);
}
if name == types::SELF {
return HmAst::Variable(
self.config.self_type_name.clone().unwrap_or_else(|| types::SELF.to_string()),
);
}
let brand_name = format_brand_name(&name, self.config);
match &segment.arguments {
PathArguments::AngleBracketed(args) => {
let mut type_args = Vec::new();
for arg in &args.args {
if let GenericArgument::Type(inner_ty) = arg {
type_args.push(self.visit(inner_ty));
}
}
if type_args.is_empty() {
HmAst::Variable(brand_name)
} else {
HmAst::Constructor(brand_name, type_args)
}
}
_ => HmAst::Variable(brand_name),
}
}
}
fn visit_macro(
&mut self,
type_macro: &syn::TypeMacro,
) -> Self::Output {
if let Some((brand, args)) = get_apply_macro_parameters(type_macro) {
let constructor_type = self.visit(&brand);
let type_args: Vec<_> = args.iter().map(|ty| self.visit(ty)).collect();
match constructor_type {
HmAst::Variable(name) =>
if type_args.is_empty() {
HmAst::Variable(name)
} else {
HmAst::Constructor(name, type_args)
},
HmAst::Constructor(name, mut prev_args) => {
prev_args.extend(type_args);
HmAst::Constructor(name, prev_args)
}
_ => {
let name = format!("{constructor_type}");
HmAst::Constructor(name, type_args)
}
}
} else {
HmAst::Variable("macro".to_string())
}
}
fn visit_reference(
&mut self,
type_ref: &syn::TypeReference,
) -> Self::Output {
let inner = self.visit(&type_ref.elem);
if type_ref.mutability.is_some() {
HmAst::MutableReference(Box::new(inner))
} else {
HmAst::Reference(Box::new(inner))
}
}
fn visit_trait_object(
&mut self,
trait_object: &syn::TypeTraitObject,
) -> Self::Output {
let mut bounds = Vec::new();
for bound in &trait_object.bounds {
if let syn::TypeParamBound::Trait(trait_bound) = bound
&& let Some(segment) = last_path_segment(&trait_bound.path)
{
let name = segment.ident.to_string();
if !self.config.ignored_traits().contains(&name) {
bounds.push(trait_bound_to_hm_type(
trait_bound,
self.fn_bounds,
self.generic_names,
self.config,
));
}
}
}
if bounds.is_empty() {
HmAst::TraitObject(Box::new(HmAst::Variable("_".to_string())))
} else {
#[expect(clippy::indexing_slicing, reason = "Length checked above")]
let inner = if bounds.len() == 1 { bounds[0].clone() } else { HmAst::Tuple(bounds) };
HmAst::TraitObject(Box::new(inner))
}
}
fn visit_impl_trait(
&mut self,
impl_trait: &syn::TypeImplTrait,
) -> Self::Output {
for bound in &impl_trait.bounds {
if let TypeParamBound::Trait(trait_bound) = bound {
return trait_bound_to_hm_type(
trait_bound,
self.fn_bounds,
self.generic_names,
self.config,
);
}
}
HmAst::Variable("impl_trait".to_string())
}
fn visit_bare_fn(
&mut self,
bare_fn: &syn::TypeBareFn,
) -> Self::Output {
let inputs: Vec<HmAst> = bare_fn.inputs.iter().map(|arg| self.visit(&arg.ty)).collect();
let output = match &bare_fn.output {
ReturnType::Default => HmAst::Unit,
ReturnType::Type(_, ty) => self.visit(ty),
};
#[expect(clippy::indexing_slicing, reason = "Length checked above")]
let input_ty = if inputs.len() == 1 { inputs[0].clone() } else { HmAst::Tuple(inputs) };
HmAst::Arrow(Box::new(input_ty), Box::new(output))
}
fn visit_tuple(
&mut self,
tuple: &syn::TypeTuple,
) -> Self::Output {
let types: Vec<HmAst> = tuple
.elems
.iter()
.filter(|t| !crate::support::is_phantom_data(t))
.map(|t| self.visit(t))
.collect();
if types.is_empty() {
HmAst::Unit
} else if types.len() == 1 {
#[expect(clippy::indexing_slicing, reason = "Length checked above")]
types[0].clone()
} else {
HmAst::Tuple(types)
}
}
fn visit_array(
&mut self,
array: &syn::TypeArray,
) -> Self::Output {
let inner = self.visit(&array.elem);
HmAst::List(Box::new(inner))
}
fn visit_slice(
&mut self,
slice: &syn::TypeSlice,
) -> Self::Output {
let inner = self.visit(&slice.elem);
HmAst::List(Box::new(inner))
}
fn visit_other(
&mut self,
_ty: &syn::Type,
) -> Self::Output {
HmAst::Variable("_".to_string())
}
}