Skip to main content

forge_pilot/
cli.rs

1use crate::loop_runner::LoopReport;
2use crate::observe::Observation;
3use crate::orient::TargetCandidate;
4use crate::repo_chat::RepoChatAnswer;
5use serde::Serialize;
6
7#[derive(Serialize)]
8struct CandidateView<'a> {
9    stable_key: &'a str,
10    urgency: f64,
11    rationale: &'a str,
12    check_hints: &'a [String],
13}
14
15/// Renders an observation report as either JSON or terminal text.
16pub fn render_observation_report(observation: &Observation, json: bool) -> String {
17    render(observation, json)
18}
19
20/// Renders scored target candidates as either JSON or terminal text.
21pub fn render_candidate_report(candidates: &[TargetCandidate], json: bool) -> String {
22    let view = candidates
23        .iter()
24        .map(|candidate| CandidateView {
25            stable_key: &candidate.stable_key,
26            urgency: candidate.urgency,
27            rationale: &candidate.rationale,
28            check_hints: &candidate.check_hints,
29        })
30        .collect::<Vec<_>>();
31    render(&view, json)
32}
33
34/// Renders a loop report as either JSON or terminal text.
35pub fn render_loop_report(report: &LoopReport, json: bool) -> String {
36    render(report, json)
37}
38
39/// Renders a repo-chat answer as either JSON or terminal text.
40pub fn render_repo_chat_answer(answer: &RepoChatAnswer, json: bool) -> String {
41    render(answer, json)
42}
43
44fn render<T: Serialize + ?Sized>(value: &T, json: bool) -> String {
45    if json {
46        serde_json::to_string_pretty(value).unwrap_or_else(|_| "{}".into())
47    } else {
48        serde_json::to_string_pretty(value).unwrap_or_else(|_| "{}".into())
49    }
50}