use ryo_mutations::basic::{AddStructLiteralFieldMutation, RemoveStructLiteralFieldMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::macro_utils;
use ryo_source::pure::{PureBlock, PureExpr, PureImplItem, PureItem, PureStmt, ToSynError};
use crate::engine::{ASTMutationContext, ASTRegApply, ModificationType};
impl ASTRegApply for AddStructLiteralFieldMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let struct_name = ctx
.symbol_registry
.path(self.struct_id)
.map(|p| p.name().to_string())
.unwrap_or_else(|| format!("{:?}", self.struct_id));
let mut total_changes = 0;
let symbol_ids: Vec<_> = ctx.symbol_registry.iter().map(|(id, _)| id).collect();
for id in symbol_ids {
if let Some(kind) = ctx.symbol_registry.kind(id) {
if kind == ryo_symbol::SymbolKind::Method {
continue;
}
}
let ast = match ctx.get_ast_mut(id) {
Some(ast) => ast,
None => continue,
};
let changes = match ast {
PureItem::Fn(f) => match walk_and_add_field(
&mut f.body,
&struct_name,
&self.field_name,
&self.value,
None,
) {
Ok(c) => c,
Err(e) => {
return MutationResult {
mutation_type: "AddStructLiteralField".to_string(),
changes: 0,
description: format!("Failed to serialize macro tokens: {}", e),
};
}
},
PureItem::Impl(impl_block) => {
let self_ty = Some(impl_block.self_ty.as_str());
let mut count = 0;
for item in &mut impl_block.items {
if let PureImplItem::Fn(method) = item {
match walk_and_add_field(
&mut method.body,
&struct_name,
&self.field_name,
&self.value,
self_ty,
) {
Ok(c) => count += c,
Err(e) => {
return MutationResult {
mutation_type: "AddStructLiteralField".to_string(),
changes: 0,
description: format!(
"Failed to serialize macro tokens: {}",
e
),
};
}
}
}
}
count
}
_ => 0,
};
if changes > 0 {
ctx.emit_modified(
id,
ModificationType::Other("StructLiteralFieldAdded".into()),
);
total_changes += changes;
}
}
MutationResult {
mutation_type: "AddStructLiteralField".to_string(),
changes: total_changes,
description: if total_changes > 0 {
format!(
"Added field '{}' to {} struct literal(s) of '{}'",
self.field_name, total_changes, struct_name
)
} else {
format!(
"No struct literals of '{}' found or field already exists",
struct_name
)
},
}
}
}
impl ASTRegApply for RemoveStructLiteralFieldMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let struct_name = ctx
.symbol_registry
.path(self.struct_id)
.map(|p| p.name().to_string())
.unwrap_or_else(|| format!("{:?}", self.struct_id));
let mut total_changes = 0;
let symbol_ids: Vec<_> = ctx.symbol_registry.iter().map(|(id, _)| id).collect();
for id in symbol_ids {
if let Some(kind) = ctx.symbol_registry.kind(id) {
if kind == ryo_symbol::SymbolKind::Method {
continue;
}
}
let ast = match ctx.get_ast_mut(id) {
Some(ast) => ast,
None => continue,
};
let changes = match ast {
PureItem::Fn(f) => {
match walk_and_remove_field(&mut f.body, &struct_name, &self.field_name, None) {
Ok(c) => c,
Err(e) => {
return MutationResult {
mutation_type: "RemoveStructLiteralField".to_string(),
changes: 0,
description: format!("Failed to serialize macro tokens: {}", e),
};
}
}
}
PureItem::Impl(impl_block) => {
let self_ty = Some(impl_block.self_ty.as_str());
let mut count = 0;
for item in &mut impl_block.items {
if let PureImplItem::Fn(method) = item {
match walk_and_remove_field(
&mut method.body,
&struct_name,
&self.field_name,
self_ty,
) {
Ok(c) => count += c,
Err(e) => {
return MutationResult {
mutation_type: "RemoveStructLiteralField".to_string(),
changes: 0,
description: format!(
"Failed to serialize macro tokens: {}",
e
),
};
}
}
}
}
count
}
_ => 0,
};
if changes > 0 {
ctx.emit_modified(
id,
ModificationType::Other("StructLiteralFieldRemoved".into()),
);
total_changes += changes;
}
}
MutationResult {
mutation_type: "RemoveStructLiteralField".to_string(),
changes: total_changes,
description: if total_changes > 0 {
format!(
"Removed field '{}' from {} struct literal(s) of '{}'",
self.field_name, total_changes, struct_name
)
} else {
format!(
"No struct literals of '{}' with field '{}' found",
struct_name, self.field_name
)
},
}
}
}
fn matches_struct(path: &str, struct_name: &str, self_ty: Option<&str>) -> bool {
if path.ends_with(struct_name) || path == struct_name {
return true;
}
if path == "Self" {
if let Some(ty) = self_ty {
return ty.ends_with(struct_name) || ty == struct_name;
}
}
false
}
fn parse_value(value: &str) -> PureExpr {
let value = value.trim();
if value == "None" {
return PureExpr::Path("None".to_string());
}
if value.starts_with("Some(") && value.ends_with(')') {
let inner = &value[5..value.len() - 1];
return PureExpr::Call {
func: Box::new(PureExpr::Path("Some".to_string())),
args: vec![PureExpr::Other(inner.to_string())],
};
}
if value == "Default::default()" {
return PureExpr::Call {
func: Box::new(PureExpr::Path("Default::default".to_string())),
args: vec![],
};
}
if value.parse::<i64>().is_ok() || value.parse::<f64>().is_ok() {
return PureExpr::Lit(value.to_string());
}
PureExpr::Other(value.to_string())
}
fn walk_and_add_field(
block: &mut PureBlock,
struct_name: &str,
field_name: &str,
value: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
let mut count = 0;
for stmt in &mut block.stmts {
count += walk_stmt_and_add_field(stmt, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
fn walk_and_remove_field(
block: &mut PureBlock,
struct_name: &str,
field_name: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
let mut count = 0;
for stmt in &mut block.stmts {
count += walk_stmt_and_remove_field(stmt, struct_name, field_name, self_ty)?;
}
Ok(count)
}
fn walk_stmt_and_add_field(
stmt: &mut PureStmt,
struct_name: &str,
field_name: &str,
value: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
match stmt {
PureStmt::Local {
init: Some(expr), ..
} => {
return walk_expr_and_add_field(expr, struct_name, field_name, value, self_ty);
}
PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
return walk_expr_and_add_field(expr, struct_name, field_name, value, self_ty);
}
_ => {}
}
Ok(0)
}
fn walk_stmt_and_remove_field(
stmt: &mut PureStmt,
struct_name: &str,
field_name: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
match stmt {
PureStmt::Local {
init: Some(expr), ..
} => {
return walk_expr_and_remove_field(expr, struct_name, field_name, self_ty);
}
PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
return walk_expr_and_remove_field(expr, struct_name, field_name, self_ty);
}
_ => {}
}
Ok(0)
}
fn walk_expr_and_add_field(
expr: &mut PureExpr,
struct_name: &str,
field_name: &str,
value: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
let mut count = 0;
if let PureExpr::Struct { path, fields } = expr {
if matches_struct(path, struct_name, self_ty) {
if !fields.iter().any(|(name, _)| name == field_name) {
fields.push((field_name.to_string(), parse_value(value)));
count += 1;
}
}
}
count += walk_expr_children_and_add_field(expr, struct_name, field_name, value, self_ty)?;
Ok(count)
}
fn walk_expr_and_remove_field(
expr: &mut PureExpr,
struct_name: &str,
field_name: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
let mut count = 0;
if let PureExpr::Struct { path, fields } = expr {
if matches_struct(path, struct_name, self_ty) {
let original_len = fields.len();
fields.retain(|(name, _)| name != field_name);
if fields.len() < original_len {
count += 1;
}
}
}
count += walk_expr_children_and_remove_field(expr, struct_name, field_name, self_ty)?;
Ok(count)
}
fn walk_expr_children_and_add_field(
expr: &mut PureExpr,
struct_name: &str,
field_name: &str,
value: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
match expr {
PureExpr::Block { block, .. } => {
walk_and_add_field(block, struct_name, field_name, value, self_ty)
}
PureExpr::If {
cond,
then_branch,
else_branch,
} => {
let mut count = walk_expr_and_add_field(cond, struct_name, field_name, value, self_ty)?;
count += walk_and_add_field(then_branch, struct_name, field_name, value, self_ty)?;
if let Some(else_expr) = else_branch {
count +=
walk_expr_and_add_field(else_expr, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::Match { expr: e, arms } => {
let mut count = walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
for arm in arms {
count += walk_expr_and_add_field(
&mut arm.body,
struct_name,
field_name,
value,
self_ty,
)?;
}
Ok(count)
}
PureExpr::Loop { body: block, .. } | PureExpr::Unsafe(block) => {
walk_and_add_field(block, struct_name, field_name, value, self_ty)
}
PureExpr::While { cond, body, .. } => {
Ok(
walk_expr_and_add_field(cond, struct_name, field_name, value, self_ty)?
+ walk_and_add_field(body, struct_name, field_name, value, self_ty)?,
)
}
PureExpr::For { expr: e, body, .. } => {
Ok(
walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
+ walk_and_add_field(body, struct_name, field_name, value, self_ty)?,
)
}
PureExpr::Async { body, .. } => {
walk_and_add_field(body, struct_name, field_name, value, self_ty)
}
PureExpr::Closure { body, .. } => {
walk_expr_and_add_field(body, struct_name, field_name, value, self_ty)
}
PureExpr::Call { func, args } => {
let mut count = walk_expr_and_add_field(func, struct_name, field_name, value, self_ty)?;
for arg in args {
count += walk_expr_and_add_field(arg, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::MethodCall { receiver, args, .. } => {
let mut count =
walk_expr_and_add_field(receiver, struct_name, field_name, value, self_ty)?;
for arg in args {
count += walk_expr_and_add_field(arg, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::Binary { left, right, .. } => {
Ok(
walk_expr_and_add_field(left, struct_name, field_name, value, self_ty)?
+ walk_expr_and_add_field(right, struct_name, field_name, value, self_ty)?,
)
}
PureExpr::Unary { expr: e, .. }
| PureExpr::Field { expr: e, .. }
| PureExpr::Await(e)
| PureExpr::Try(e) => walk_expr_and_add_field(e, struct_name, field_name, value, self_ty),
PureExpr::Index { expr: e, index } => {
Ok(
walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
+ walk_expr_and_add_field(index, struct_name, field_name, value, self_ty)?,
)
}
PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
let mut count = 0;
for e in exprs {
count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::Return(Some(e)) | PureExpr::Break { expr: Some(e), .. } => {
walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)
}
PureExpr::Let { expr: e, .. }
| PureExpr::Cast { expr: e, .. }
| PureExpr::Ref { expr: e, .. } => {
walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)
}
PureExpr::Struct { fields, .. } => {
let mut count = 0;
for (_, field_expr) in fields {
count +=
walk_expr_and_add_field(field_expr, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::Range { start, end, .. } => {
let mut count = 0;
if let Some(s) = start {
count += walk_expr_and_add_field(s, struct_name, field_name, value, self_ty)?;
}
if let Some(e) = end {
count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
}
Ok(count)
}
PureExpr::Repeat { expr: e, len } => {
Ok(
walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
+ walk_expr_and_add_field(len, struct_name, field_name, value, self_ty)?,
)
}
PureExpr::Macro { name, tokens, .. } => {
if let Some(mut exprs) = macro_utils::try_extract_exprs(name, tokens) {
let mut count = 0;
for e in &mut exprs {
count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
}
if count > 0 {
*tokens = macro_utils::exprs_to_tokens(&exprs)?;
}
Ok(count)
} else {
Ok(0)
}
}
_ => Ok(0),
}
}
fn walk_expr_children_and_remove_field(
expr: &mut PureExpr,
struct_name: &str,
field_name: &str,
self_ty: Option<&str>,
) -> Result<usize, ToSynError> {
match expr {
PureExpr::Block { block, .. } => {
walk_and_remove_field(block, struct_name, field_name, self_ty)
}
PureExpr::If {
cond,
then_branch,
else_branch,
} => {
let mut count = walk_expr_and_remove_field(cond, struct_name, field_name, self_ty)?;
count += walk_and_remove_field(then_branch, struct_name, field_name, self_ty)?;
if let Some(else_expr) = else_branch {
count += walk_expr_and_remove_field(else_expr, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Match { expr: e, arms } => {
let mut count = walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
for arm in arms {
count +=
walk_expr_and_remove_field(&mut arm.body, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Loop { body: block, .. } | PureExpr::Unsafe(block) => {
walk_and_remove_field(block, struct_name, field_name, self_ty)
}
PureExpr::While { cond, body, .. } => {
Ok(
walk_expr_and_remove_field(cond, struct_name, field_name, self_ty)?
+ walk_and_remove_field(body, struct_name, field_name, self_ty)?,
)
}
PureExpr::For { expr: e, body, .. } => {
Ok(
walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
+ walk_and_remove_field(body, struct_name, field_name, self_ty)?,
)
}
PureExpr::Async { body, .. } => {
walk_and_remove_field(body, struct_name, field_name, self_ty)
}
PureExpr::Closure { body, .. } => {
walk_expr_and_remove_field(body, struct_name, field_name, self_ty)
}
PureExpr::Call { func, args } => {
let mut count = walk_expr_and_remove_field(func, struct_name, field_name, self_ty)?;
for arg in args {
count += walk_expr_and_remove_field(arg, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::MethodCall { receiver, args, .. } => {
let mut count = walk_expr_and_remove_field(receiver, struct_name, field_name, self_ty)?;
for arg in args {
count += walk_expr_and_remove_field(arg, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Binary { left, right, .. } => {
Ok(
walk_expr_and_remove_field(left, struct_name, field_name, self_ty)?
+ walk_expr_and_remove_field(right, struct_name, field_name, self_ty)?,
)
}
PureExpr::Unary { expr: e, .. }
| PureExpr::Field { expr: e, .. }
| PureExpr::Await(e)
| PureExpr::Try(e) => walk_expr_and_remove_field(e, struct_name, field_name, self_ty),
PureExpr::Index { expr: e, index } => {
Ok(
walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
+ walk_expr_and_remove_field(index, struct_name, field_name, self_ty)?,
)
}
PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
let mut count = 0;
for e in exprs {
count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Return(Some(e)) | PureExpr::Break { expr: Some(e), .. } => {
walk_expr_and_remove_field(e, struct_name, field_name, self_ty)
}
PureExpr::Let { expr: e, .. }
| PureExpr::Cast { expr: e, .. }
| PureExpr::Ref { expr: e, .. } => {
walk_expr_and_remove_field(e, struct_name, field_name, self_ty)
}
PureExpr::Struct { fields, .. } => {
let mut count = 0;
for (_, field_expr) in fields {
count += walk_expr_and_remove_field(field_expr, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Range { start, end, .. } => {
let mut count = 0;
if let Some(s) = start {
count += walk_expr_and_remove_field(s, struct_name, field_name, self_ty)?;
}
if let Some(e) = end {
count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
}
Ok(count)
}
PureExpr::Repeat { expr: e, len } => {
Ok(
walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
+ walk_expr_and_remove_field(len, struct_name, field_name, self_ty)?,
)
}
PureExpr::Macro { name, tokens, .. } => {
if let Some(mut exprs) = macro_utils::try_extract_exprs(name, tokens) {
let mut count = 0;
for e in &mut exprs {
count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
}
if count > 0 {
*tokens = macro_utils::exprs_to_tokens(&exprs)?;
}
Ok(count)
} else {
Ok(0)
}
}
_ => Ok(0),
}
}