chant/operations/
update.rs1use anyhow::Result;
6use std::path::Path;
7
8use crate::spec::{Spec, SpecStatus, TransitionBuilder};
9
10#[derive(Debug, Clone, Default)]
12pub struct UpdateOptions {
13 pub status: Option<SpecStatus>,
15 pub depends_on: Option<Vec<String>>,
17 pub labels: Option<Vec<String>>,
19 pub target_files: Option<Vec<String>>,
21 pub model: Option<String>,
23 pub output: Option<String>,
25 pub force: bool,
27}
28
29pub fn update_spec(spec: &mut Spec, spec_path: &Path, options: UpdateOptions) -> Result<()> {
34 let mut updated = false;
35
36 if let Some(new_status) = options.status {
38 let mut builder = TransitionBuilder::new(spec);
39 if options.force {
40 builder = builder.force();
41 }
42 builder.to(new_status)?;
43 updated = true;
44 }
45
46 if let Some(depends_on) = options.depends_on {
48 spec.frontmatter.depends_on = Some(depends_on);
49 updated = true;
50 }
51
52 if let Some(labels) = options.labels {
54 spec.frontmatter.labels = Some(labels);
55 updated = true;
56 }
57
58 if let Some(target_files) = options.target_files {
60 spec.frontmatter.target_files = Some(target_files);
61 updated = true;
62 }
63
64 if let Some(model) = options.model {
66 spec.frontmatter.model = Some(model);
67 updated = true;
68 }
69
70 if let Some(output) = options.output {
72 if !output.is_empty() {
73 if !spec.body.ends_with('\n') && !spec.body.is_empty() {
74 spec.body.push('\n');
75 }
76 spec.body.push_str("\n## Output\n\n");
77 spec.body.push_str(&output);
78 spec.body.push('\n');
79 updated = true;
80 }
81 }
82
83 if !updated {
84 anyhow::bail!("No updates specified");
85 }
86
87 spec.save(spec_path)?;
89
90 Ok(())
91}