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 replace_body: bool,
27 pub force: bool,
29}
30
31pub fn update_spec(spec: &mut Spec, spec_path: &Path, options: UpdateOptions) -> Result<()> {
36 let mut updated = false;
37
38 if let Some(new_status) = options.status {
40 let mut builder = TransitionBuilder::new(spec);
41 if options.force {
42 builder = builder.force();
43 }
44 builder.to(new_status)?;
45 updated = true;
46 }
47
48 if let Some(depends_on) = options.depends_on {
50 spec.frontmatter.depends_on = Some(depends_on);
51 updated = true;
52 }
53
54 if let Some(labels) = options.labels {
56 spec.frontmatter.labels = Some(labels);
57 updated = true;
58 }
59
60 if let Some(target_files) = options.target_files {
62 spec.frontmatter.target_files = Some(target_files);
63 updated = true;
64 }
65
66 if let Some(model) = options.model {
68 spec.frontmatter.model = Some(model);
69 updated = true;
70 }
71
72 if let Some(output) = options.output {
74 if !output.is_empty() {
75 if options.replace_body {
76 spec.body = output.clone();
78 if !spec.body.ends_with('\n') {
79 spec.body.push('\n');
80 }
81 } else {
82 if !spec.body.ends_with('\n') && !spec.body.is_empty() {
84 spec.body.push('\n');
85 }
86 spec.body.push_str("\n## Output\n\n");
87 spec.body.push_str(&output);
88 spec.body.push('\n');
89 }
90 updated = true;
91 }
92 }
93
94 if !updated {
95 anyhow::bail!("No updates specified");
96 }
97
98 spec.save(spec_path)?;
100
101 Ok(())
102}