Skip to main content

git_policy_cli/
standards_review_entry.rs

1//! What:
2//!  The log entries of the standards-review shadow run, rendered as the root `mise.toml` task `shadow-review` writes
3//!  them, and their append to the log file.
4//! Why:
5//!  `doc/audit/standards-review-gate-shadow.md` already holds that task's entries, and readers and scorers parse them,
6//!  so the port writes the same lines. The script appends line by line; this port appends each entry in one write, so
7//!  entries of two concurrent runs cannot interleave.
8//!
9//! In TS you'd write (pseudocode):
10//! ```ts
11//! entry('- Date:'); entry(`  ${new Date().toISOString()}`); // ...
12//! ```
13
14use super::standards_review_request::ROUTE;
15use super::standards_review_input::CAP_LINES;
16use super::standards_review_text::on_one_line;
17use std::io::Write;
18use std::path::Path;
19
20/// Leading characters of the commit name the log prints.
21pub const SHORT_SHA_LENGTH: usize = 12;
22
23/// What:
24///  Everything one review entry prints.
25/// Why:
26///   The renderer is a pure function of these values, so a test can fix the clock and the elapsed time.
27///
28/// In TS you'd write (pseudocode):
29/// ```ts
30/// type ReviewEntry = { date: string; sha: string; subject: string; codeFiles: number; diffLines: number; truncated: boolean; inputUnits: number; seconds: string; top: string; outcome: string };
31/// ```
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct ReviewEntry {
34    /// `toISOString` text of the moment the entry is written.
35    pub date: String,
36    /// Full commit name.
37    pub sha: String,
38    /// Subject line.
39    pub subject: String,
40    /// Code files in the commit.
41    pub code_files: usize,
42    /// Lines of the whole patch.
43    pub diff_lines: usize,
44    /// Whether the patch was over the cap.
45    pub truncated: bool,
46    /// Reported model input length in UTF-16 units.
47    pub input_units: usize,
48    /// One-decimal seconds of the gateway call.
49    pub seconds: String,
50    /// The judged choice, or empty.
51    pub top: String,
52    /// The `Findings kept` line.
53    pub outcome: String,
54}
55
56/// What:
57///  The first `SHORT_SHA_LENGTH` characters of a commit name.
58/// Why:
59///   The log names commits by that prefix.
60///
61/// In TS you'd write (pseudocode):
62/// ```ts
63/// sha.slice(0, 12)
64/// ```
65fn short(sha: &str) -> String {
66    return sha.chars().take(SHORT_SHA_LENGTH).collect();
67}
68
69/// What:
70///  The three-line entry for a commit with no code files.
71/// Why:
72///   Coverage stays visible: every skipped landing leaves a line.
73///
74/// In TS you'd write (pseudocode):
75/// ```ts
76/// entry('- Skipped:'); entry(`  ${sha.slice(0, 12)} (no code files):`); entry(`  \`${subject}\``);
77/// ```
78pub fn render_skipped(sha: &str, subject: &str) -> String {
79    return format!("- Skipped:\n  {} (no code files):\n  `{}`\n", short(sha), on_one_line(subject));
80}
81
82/// What:
83///  The review entry, ending in the blank line that separates entries.
84/// Why:
85///   `Named superlinear cost` is the choice for `yes` and `no`, the two-line gesture for `partial`, and `unjudged` for
86///   anything else, as the script prints it.
87///
88/// In TS you'd write (pseudocode):
89/// ```ts
90/// entry('- Date:'); // ... entry('')
91/// ```
92pub fn render_review(entry: &ReviewEntry) -> String {
93    let capped: String = if entry.truncated { format!(", capped at {CAP_LINES}") } else { String::new() };
94    let judged: String = match entry.top.as_str() {
95        "partial" => String::from("  partial (cost gestured as unverified,\n  not named)"),
96        "yes" | "no" => format!("  {}", entry.top),
97        _ => String::from("  unjudged"),
98    };
99    return format!(
100        "- Date:\n  {date}\n  Subject:\n  {sha} `{subject}`\n  Input size:\n  `{files} code files, {lines} diff lines{capped}, luna input {units} chars`\n  Reviewer route:\n  `{ROUTE}`\n  Wall-clock cost:\n  {seconds} s\n  Named superlinear cost:\n{judged}\n  Findings kept:\n    `````text\n    {outcome}\n    `````\n\n",
101        date = entry.date,
102        sha = short(entry.sha.as_str()),
103        subject = on_one_line(entry.subject.as_str()),
104        files = entry.code_files,
105        lines = entry.diff_lines,
106        units = entry.input_units,
107        seconds = entry.seconds,
108        outcome = on_one_line(entry.outcome.as_str()),
109    );
110}
111
112/// What:
113///  Append `text` to the log file, creating it when missing, in one write.
114/// Why:
115///   The log is shared by every run; an append-mode file takes one write whole, where the script's per-line appends of
116///   two runs could interleave.
117///
118/// In TS you'd write (pseudocode):
119/// ```ts
120/// appendFileSync(logPath, text)
121/// ```
122pub fn append_entry(log_path: &Path, text: &str) -> std::io::Result<()> {
123    let mut file: std::fs::File = std::fs::OpenOptions::new().append(true).create(true).open(log_path)?;
124    return file.write_all(text.as_bytes());
125}
126
127/// Controls stay out of the release executable.
128#[cfg(test)]
129#[path = "standards_review_entry_tests.rs"]
130mod tests;