speccoder_runtime/
quick.rs1use std::path::Path;
9
10use speccoder_core::{CommandResult, CommandStatus};
11
12#[must_use]
14pub fn change(
15 path: &Path,
16 request: &str,
17 checks: &[String],
18 run: bool,
19 via: &str,
20 options: &crate::RunOptions,
21) -> CommandResult {
22 let format = crate::format::at(path);
23 let Some(root) = format.detect(path) else {
24 return CommandResult::new(
25 "do",
26 CommandStatus::NotAProject,
27 &(format.messages().not_a_project)(path),
28 )
29 .with_code("NO-PROJECT");
30 };
31 let rec = match format.record_change(&root, request, checks) {
32 Ok(r) => r,
33 Err(e) => {
34 return CommandResult::new("do", CommandStatus::UsageError, &e).with_code("USAGE");
35 }
36 };
37 let approved = crate::review::decide(
39 &root,
40 Some(&rec.feature),
41 std::slice::from_ref(&rec.requirement),
42 "approve",
43 None,
44 via,
45 );
46 if approved.status() != CommandStatus::Ok {
47 return CommandResult::carried("do", &approved);
48 }
49 let files = crate::hosting::uncommitted(&root, Some(&rec.feature));
51 if !files.is_empty() {
52 let committed = crate::hosting::commit_planning_as(
53 &root,
54 Some(&rec.feature),
55 &files,
56 Some(&format!("quick change: {}", rec.title)),
57 );
58 if committed.status() != CommandStatus::Ok {
59 return CommandResult::carried("do", &committed);
60 }
61 }
62 let said = format!(
63 "recorded \"{}\" as {} and {} in {}",
64 rec.title, rec.requirement, rec.task, rec.feature
65 );
66 if !run {
67 return CommandResult::ok(
68 "do",
69 &format!(
70 "{said}; run it: speccoder run --feature {} --task {}",
71 rec.feature, rec.task
72 ),
73 )
74 .with_next(vec![speccoder_core::next::NextStep {
75 command: format!(
76 "speccoder run --feature {} --task {}",
77 rec.feature, rec.task
78 ),
79 why: "the change, judged on evidence".to_owned(),
80 }]);
81 }
82 let result = crate::run(&root, Some(&rec.feature), &rec.task, options);
83 let status = result.status();
84 let message = format!("{said}; {}", result.message());
85 result.reworded(status, &message)
86}