#[derive(Debug, PartialEq, Eq)]
pub enum RegisterOutcome {
Inserted,
AlreadyRegistered,
AnchorNotFound,
TargetMissing,
Skipped,
}
pub fn insert_chain_call_after_anchor(
contents: &mut String,
anchor_substring: &str,
new_call: &str,
) -> bool {
let lines: Vec<&str> = contents.lines().collect();
let pos = lines
.iter()
.enumerate()
.rev()
.find(|(_, l)| l.contains(anchor_substring))
.map(|(i, _)| i);
let Some(i) = pos else {
return false;
};
let anchor = lines[i];
let anchor_ws: String = anchor.chars().take_while(|c| c.is_whitespace()).collect();
let trimmed = anchor.trim_start();
let indent = if trimmed.starts_with('.') {
anchor_ws
} else {
format!("{anchor_ws} ")
};
let new_line = format!("{indent}{new_call}");
let mut new_lines: Vec<String> = lines.iter().map(|s| (*s).to_string()).collect();
new_lines.insert(i + 1, new_line);
*contents = new_lines.join("\n");
if !contents.ends_with('\n') {
contents.push('\n');
}
true
}
pub fn insert_use_after_last_use(contents: &mut String, new_use: &str) -> bool {
let lines: Vec<&str> = contents.lines().collect();
let pos = lines
.iter()
.enumerate()
.rev()
.find(|(_, l)| l.trim_start().starts_with("use "))
.map(|(i, _)| i);
let Some(i) = pos else {
return false;
};
let mut new_lines: Vec<String> = lines.iter().map(|s| (*s).to_string()).collect();
new_lines.insert(i + 1, new_use.to_string());
*contents = new_lines.join("\n");
if !contents.ends_with('\n') {
contents.push('\n');
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chain_insert_after_opener_indents_plus_four() {
let mut src = "bootstrap()\n .listen(addr)\n .await\n".to_string();
let inserted =
insert_chain_call_after_anchor(&mut src, "bootstrap()", ".module(foo::define())");
assert!(inserted);
assert!(
src.contains("\n .module(foo::define())\n .listen"),
"got: {src}"
);
}
#[test]
fn chain_insert_after_chain_element_keeps_same_indent() {
let mut src =
"bootstrap()\n .module(a::define())\n .listen(addr)\n .await\n".to_string();
let inserted = insert_chain_call_after_anchor(&mut src, ".module(", ".module(b::define())");
assert!(inserted);
assert!(
src.contains(" .module(a::define())\n .module(b::define())\n .listen"),
"got: {src}"
);
}
#[test]
fn chain_insert_returns_false_when_anchor_missing() {
let mut src = "fn main() {}\n".to_string();
let inserted = insert_chain_call_after_anchor(&mut src, "bootstrap()", ".module(x)");
assert!(!inserted);
assert_eq!(src, "fn main() {}\n");
}
#[test]
fn chain_insert_picks_last_anchor_when_multiple() {
let mut src =
"bootstrap()\n .module(a)\n .module(b)\n .listen(addr)\n".to_string();
insert_chain_call_after_anchor(&mut src, ".module(", ".module(c)");
assert!(
src.contains(" .module(b)\n .module(c)\n .listen"),
"got: {src}"
);
}
#[test]
fn use_insert_after_last_use_line() {
let mut src = "use std::sync::Arc;\nuse kick_rs::*;\n\nfn main() {}\n".to_string();
let inserted = insert_use_after_last_use(&mut src, "use foo::Foo;");
assert!(inserted);
assert!(
src.contains("use kick_rs::*;\nuse foo::Foo;\n\nfn main()"),
"got: {src}"
);
}
#[test]
fn use_insert_returns_false_when_no_use_lines() {
let mut src = "fn main() {}\n".to_string();
let inserted = insert_use_after_last_use(&mut src, "use foo::Foo;");
assert!(!inserted);
assert_eq!(src, "fn main() {}\n");
}
}