chant/operations/
archive.rs1use anyhow::{Context, Result};
6use std::path::{Path, PathBuf};
7
8use crate::paths::ARCHIVE_DIR;
9use crate::spec::SpecStatus;
10
11#[derive(Debug, Clone, Default)]
13pub struct ArchiveOptions {
14 pub no_stage: bool,
16 pub allow_non_completed: bool,
18}
19
20fn is_git_repo() -> bool {
22 std::process::Command::new("git")
23 .args(["rev-parse", "--git-dir"])
24 .output()
25 .map(|output| output.status.success())
26 .unwrap_or(false)
27}
28
29pub fn move_spec_file(src: &PathBuf, dst: &PathBuf, no_stage: bool) -> Result<()> {
31 let use_git = !no_stage && is_git_repo();
32
33 if use_git {
34 let status = std::process::Command::new("git")
36 .args(["mv", &src.to_string_lossy(), &dst.to_string_lossy()])
37 .status()
38 .context("Failed to run git mv")?;
39
40 if !status.success() {
41 eprintln!(
43 "Warning: git mv failed for {} (file may be untracked), using filesystem move",
44 src.display()
45 );
46 std::fs::rename(src, dst).context(format!(
47 "Failed to move file from {} to {}",
48 src.display(),
49 dst.display()
50 ))?;
51 }
52 } else {
53 std::fs::rename(src, dst).context(format!(
55 "Failed to move file from {} to {}",
56 src.display(),
57 dst.display()
58 ))?;
59 }
60
61 Ok(())
62}
63
64pub fn archive_spec(specs_dir: &Path, spec_id: &str, options: &ArchiveOptions) -> Result<PathBuf> {
80 use crate::spec;
81
82 let spec = spec::resolve_spec(specs_dir, spec_id)?;
84
85 if spec.frontmatter.status != SpecStatus::Completed && !options.allow_non_completed {
87 anyhow::bail!(
88 "Spec '{}' must be completed to archive (current: {:?})",
89 spec.id,
90 spec.frontmatter.status
91 );
92 }
93
94 let archive_dir = PathBuf::from(ARCHIVE_DIR);
95
96 let date_part = &spec.id[..10]; let date_dir = archive_dir.join(date_part);
99
100 if !date_dir.exists() {
102 std::fs::create_dir_all(&date_dir)?;
103 }
104
105 let source_path = specs_dir.join(format!("{}.md", spec.id));
106 let dest_path = date_dir.join(format!("{}.md", spec.id));
107
108 move_spec_file(&source_path, &dest_path, options.no_stage)?;
110
111 Ok(dest_path)
112}