use crate::EmitEffects;
use crate::Planner;
use crate::expressions::top_items::emit_doc;
use crate::names::go_name;
use syntax::ast::{Expression, Generic, Pattern, Visibility};
use syntax::types::Type;
struct ImplContext<'a> {
receiver_name: &'a str,
ty: &'a Type,
generics: &'a [Generic],
qualified_type: String,
}
impl Planner<'_> {
pub(crate) fn emit_impl_block(
&mut self,
receiver_name: &str,
ty: &Type,
methods: &[Expression],
generics: &[Generic],
fx: &mut EmitEffects,
) -> String {
let ctx = ImplContext {
receiver_name,
ty,
generics,
qualified_type: self.facts.qualified_current(receiver_name),
};
methods
.iter()
.filter_map(|method| self.emit_impl_method(method, &ctx, fx))
.collect::<Vec<_>>()
.join("\n\n")
}
fn emit_impl_method(
&mut self,
method: &Expression,
ctx: &ImplContext<'_>,
fx: &mut EmitEffects,
) -> Option<String> {
let Expression::Function {
doc,
visibility,
name_span,
..
} = method
else {
return None;
};
if self.facts.is_unused_definition(name_span) {
return None;
}
self.scope.reset_for_top_level();
let function = method.to_function_definition();
let is_public = matches!(visibility, Visibility::Public);
let has_self = function.params.first().is_some_and(|p| {
matches!(p.pattern, Pattern::Identifier { ref identifier, .. } if identifier == "self")
});
let is_ufcs = self
.facts
.is_ufcs_method(&ctx.qualified_type, &function.name);
let should_export = is_public || self.method_needs_export(&function.name);
let is_free_function = !has_self || is_ufcs;
let code = if is_free_function {
let mut free_function = function.clone();
let method_name = if should_export {
go_name::snake_to_camel(&function.name)
} else {
function.name.to_string()
};
free_function.name = format!("{}_{}", ctx.receiver_name, method_name).into();
let mut combined_generics = ctx.generics.to_vec();
combined_generics.extend(free_function.generics.iter().cloned());
free_function.generics = combined_generics;
self.emit_function(&free_function, None, false, fx)
} else {
self.emit_function(
&function,
Some((ctx.receiver_name.to_string(), ctx.ty.clone())),
should_export,
fx,
)
};
if code.is_empty() {
return None;
}
let method_doc_comment = emit_doc(doc);
Some(format!("{}{}", method_doc_comment, code))
}
}