use camino::Utf8PathBuf;
use crate::applied::AppliedState;
use crate::error::{Error, Result};
use super::resolve_pj_root;
pub async fn run(template_name: String, at: Option<Utf8PathBuf>, no_color: bool) -> Result<()> {
let _ = no_color;
let cwd = resolve_pj_root(at)?;
let pj_root = crate::paths::find_pj_root(&cwd).ok_or_else(|| {
Error::Config(format!(
"no .kata/applied.toml found at or above {cwd}; run `kata init` first"
))
})?;
let mut applied = AppliedState::load(&pj_root)?;
let matches: Vec<String> = applied
.templates
.iter()
.filter(|t| template_matches(&t.source, &template_name))
.map(|t| t.source.clone())
.collect();
match matches.len() {
0 => {
return Err(Error::Config(format!(
"template `{template_name}` is not applied to this project; nothing to remove"
)));
}
1 => {}
_ => {
let listed = matches.join(", ");
return Err(Error::Config(format!(
"template `{template_name}` is ambiguous — matches: {listed}. Pass the full source spec."
)));
}
}
applied
.templates
.retain(|t| !template_matches(&t.source, &template_name));
applied.save(&pj_root)?;
println!(
"removed `{template_name}` from {}/.kata/applied.toml",
pj_root
);
println!(
"(files written by the template stay in place — Phase 4 `--clean` will offer a delete pass)"
);
Ok(())
}
fn template_matches(source: &str, query: &str) -> bool {
if source == query {
return true;
}
source
.rsplit('/')
.next()
.map(|s| s == query)
.unwrap_or(false)
}
pub(crate) fn template_matches_pub(source: &str, query: &str) -> bool {
template_matches(source, query)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_exact_source() {
assert!(template_matches("github.com/x/y", "github.com/x/y"));
}
#[test]
fn matches_trailing_segment() {
assert!(template_matches("github.com/yukimemi/pj-rust", "pj-rust"));
assert!(template_matches("./local/pj-base", "pj-base"));
}
#[test]
fn does_not_match_unrelated() {
assert!(!template_matches("github.com/yukimemi/pj-rust", "pj-base"));
}
}