use ryo_analysis::cascade::{CascadeSpec, Visibility as CascadeVisibility};
use super::spec::{InsertPosition, MutationSpec, MutationTargetSymbol, Visibility};
impl From<CascadeSpec> for MutationSpec {
fn from(cascade: CascadeSpec) -> Self {
match cascade {
CascadeSpec::AddDerive { symbol_id, derives } => MutationSpec::AddDerive {
target: MutationTargetSymbol::ById(symbol_id),
derives,
},
CascadeSpec::GenerateImpl {
target,
trait_name,
call_new,
} => {
let body = if call_new {
format!("Self {{ inner: {}::new() }}", target.name())
} else {
"todo!()".to_string()
};
let parent = target.parent().unwrap_or_else(|| {
ryo_analysis::SymbolPath::parse("test_crate")
.expect("literal 'test_crate' is a valid SymbolPath")
});
MutationSpec::AddItem {
target: MutationTargetSymbol::ByPath(Box::new(parent)),
content: format!(
"impl {} for {} {{\n fn default() -> Self {{\n {}\n }}\n}}",
trait_name,
target.name(),
body
),
position: InsertPosition::Bottom,
}
}
CascadeSpec::ChangeVisibility {
symbol_id,
visibility,
} => MutationSpec::ChangeVisibility {
target: MutationTargetSymbol::ById(symbol_id),
visibility: convert_visibility(visibility),
},
CascadeSpec::AddUse {
target_module,
path,
} => MutationSpec::AddItem {
target: MutationTargetSymbol::ByPath(Box::new(target_module)),
content: format!("use {};", path),
position: InsertPosition::Top,
},
CascadeSpec::AddMatchArm {
target,
function_name,
enum_name,
pattern,
body,
} => {
let fn_path = target
.child(&function_name)
.unwrap_or_else(|_| target.clone());
MutationSpec::AddMatchArm {
target: MutationTargetSymbol::ByPath(Box::new(fn_path)),
enum_name,
pattern,
body,
}
}
CascadeSpec::RemoveMatchArm {
target,
function_name,
enum_name,
pattern,
} => {
let fn_path = target
.child(&function_name)
.unwrap_or_else(|_| target.clone());
MutationSpec::RemoveMatchArm {
target: MutationTargetSymbol::ByPath(Box::new(fn_path)),
enum_name,
pattern,
}
}
}
}
}
fn convert_visibility(vis: CascadeVisibility) -> Visibility {
match vis {
CascadeVisibility::Private => Visibility::Private,
CascadeVisibility::Crate => Visibility::PubCrate,
CascadeVisibility::Super => Visibility::PubSuper,
CascadeVisibility::Public => Visibility::Pub,
}
}
pub fn convert_cascade_specs(specs: Vec<CascadeSpec>) -> Vec<MutationSpec> {
specs.into_iter().map(Into::into).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use ryo_analysis::SymbolPath;
use ryo_symbol::SymbolId;
fn dummy_id(index: u32) -> SymbolId {
SymbolId::parse(&format!("{}v1", index)).expect("valid dummy id")
}
#[test]
fn test_add_derive_conversion() {
let _path = SymbolPath::parse("test_crate::Config").unwrap();
let symbol_id = dummy_id(1);
let cascade = CascadeSpec::AddDerive {
symbol_id,
derives: vec!["Default".to_string(), "Debug".to_string()],
};
let mutation: MutationSpec = cascade.into();
match mutation {
MutationSpec::AddDerive {
target, derives, ..
} => {
assert_eq!(target, MutationTargetSymbol::ById(symbol_id));
assert_eq!(derives, vec!["Default", "Debug"]);
}
_ => panic!("Expected AddDerive"),
}
}
#[test]
fn test_change_visibility_conversion() {
let _path = SymbolPath::parse("test_crate::internal::Helper").unwrap();
let symbol_id = dummy_id(2);
let cascade = CascadeSpec::ChangeVisibility {
symbol_id,
visibility: CascadeVisibility::Public,
};
let mutation: MutationSpec = cascade.into();
match mutation {
MutationSpec::ChangeVisibility {
target, visibility, ..
} => {
assert_eq!(target, MutationTargetSymbol::ById(symbol_id));
assert!(matches!(visibility, Visibility::Pub));
}
_ => panic!("Expected ChangeVisibility"),
}
}
#[test]
fn test_add_use_conversion() {
let path = SymbolPath::parse("test_crate::module").unwrap();
let cascade = CascadeSpec::AddUse {
target_module: path.clone(),
path: "std::collections::HashMap".to_string(),
};
let mutation: MutationSpec = cascade.into();
match mutation {
MutationSpec::AddItem {
target,
content,
position,
} => {
if let MutationTargetSymbol::ByPath(target_path) = target {
assert_eq!(*target_path, path);
} else {
panic!("Expected ByPath target");
}
assert_eq!(content, "use std::collections::HashMap;");
assert_eq!(position, InsertPosition::Top);
}
_ => panic!("Expected AddItem"),
}
}
}