use crate::engine::ASTRegApply;
use crate::executor::registry::converters::ResolveTargetSymbol;
use crate::executor::registry::{ConvertError, MutationConverter};
use crate::executor::spec::MutationSpec;
use ryo_analysis::AnalysisContext;
use ryo_mutations::AddPureItemsMutation;
use ryo_source::pure::PureItem;
use ryo_symbol::SymbolKind;
#[derive(Debug, Clone, Default)]
pub struct DuplicateConverter;
impl DuplicateConverter {
pub fn new() -> Self {
Self
}
}
impl ResolveTargetSymbol for DuplicateConverter {}
impl MutationConverter for DuplicateConverter {
fn spec_kinds(&self) -> &'static [&'static str] {
&[
"DuplicateFunction",
"DuplicateStruct",
"DuplicateEnum",
"DuplicateModTree",
]
}
fn convert_v2(
&self,
spec: &MutationSpec,
ctx: &AnalysisContext,
) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
match spec {
MutationSpec::DuplicateFunction {
target: target_symbol,
to,
} => {
let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
let source_ast = ctx
.ast_registry
.get(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let mut new_fn = match source_ast {
PureItem::Fn(f) => f.clone(),
_ => {
return Err(ConvertError::Apply(format!(
"Symbol {:?} is not a function",
symbol_id
)))
}
};
new_fn.name = to.clone();
let source_path = ctx
.registry
.path(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let parent_path = source_path.parent().ok_or_else(|| {
ConvertError::Apply("Source function has no parent module".to_string())
})?;
let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
})?;
Ok(vec![Box::new(AddPureItemsMutation::new(
parent_id,
vec![PureItem::Fn(new_fn)],
))])
}
MutationSpec::DuplicateStruct {
target: target_symbol,
to,
include_impls,
} => {
let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
let source_ast = ctx
.ast_registry
.get(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let (mut new_struct, source_name) = match source_ast {
PureItem::Struct(s) => (s.clone(), s.name.clone()),
_ => {
return Err(ConvertError::Apply(format!(
"Symbol {:?} is not a struct",
symbol_id
)))
}
};
new_struct.name = to.clone();
let mut items = vec![PureItem::Struct(new_struct)];
if *include_impls {
for (impl_id, _) in ctx.registry.iter() {
if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
if imp.self_ty == source_name {
let mut new_impl = imp.clone();
new_impl.self_ty = to.clone();
items.push(PureItem::Impl(new_impl));
}
}
}
}
}
let source_path = ctx
.registry
.path(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let parent_path = source_path.parent().ok_or_else(|| {
ConvertError::Apply("Source struct has no parent module".to_string())
})?;
let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
})?;
Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
}
MutationSpec::DuplicateEnum {
target: target_symbol,
to,
include_impls,
} => {
let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
let source_ast = ctx
.ast_registry
.get(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let (mut new_enum, source_name) = match source_ast {
PureItem::Enum(e) => (e.clone(), e.name.clone()),
_ => {
return Err(ConvertError::Apply(format!(
"Symbol {:?} is not an enum",
symbol_id
)))
}
};
new_enum.name = to.clone();
let mut items = vec![PureItem::Enum(new_enum)];
if *include_impls {
for (impl_id, _) in ctx.registry.iter() {
if ctx.registry.kind(impl_id) == Some(SymbolKind::Impl) {
if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
if imp.self_ty == source_name {
let mut new_impl = imp.clone();
new_impl.self_ty = to.clone();
items.push(PureItem::Impl(new_impl));
}
}
}
}
}
let source_path = ctx
.registry
.path(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let parent_path = source_path.parent().ok_or_else(|| {
ConvertError::Apply("Source enum has no parent module".to_string())
})?;
let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
})?;
Ok(vec![Box::new(AddPureItemsMutation::new(parent_id, items))])
}
MutationSpec::DuplicateModTree {
target: target_symbol,
to,
} => {
let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
let source_ast = ctx
.ast_registry
.get(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let mut new_mod = match source_ast {
PureItem::Mod(m) => m.clone(),
_ => {
return Err(ConvertError::Apply(format!(
"Symbol {:?} is not a module",
symbol_id
)))
}
};
new_mod.name = to.clone();
let source_path = ctx
.registry
.path(symbol_id)
.ok_or(ConvertError::SymbolNotFound(symbol_id))?;
let parent_path = source_path.parent().ok_or_else(|| {
ConvertError::Apply("Source module has no parent module".to_string())
})?;
let parent_id = ctx.registry.lookup(&parent_path).ok_or_else(|| {
ConvertError::Apply(format!("Parent module '{}' not found", parent_path))
})?;
Ok(vec![Box::new(AddPureItemsMutation::new(
parent_id,
vec![PureItem::Mod(new_mod)],
))])
}
_ => Err(ConvertError::TypeMismatch {
expected: "Duplicate*",
actual: spec.kind_name().to_string(),
}),
}
}
}