Skip to main content

backhopper_core/model/
summary.rs

1// Copyright (C) 2026 Michael S. Klishin and Contributors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Compact per-result projection emitted by `--formatter summary`
6//! and `--formatter text-summary`.
7
8use std::num::NonZeroU32;
9
10use serde::{Deserialize, Serialize};
11
12use crate::model::names::{CommitSha, SeriesName, vocabulary};
13use crate::model::verdict::{TouchedKinds, Verdict};
14
15/// One row in the summary output: a compact projection of a single
16/// `SeriesEvaluation` result. JSON and text formatters share the type.
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
19pub struct SummaryRow {
20    pub sha: CommitSha,
21    pub verdict: VerdictKind,
22    pub touched: TouchedKinds,
23    /// Total tracked-dep symbol references summed over every evaluated
24    /// pin. Zero means the patch references no tracked-dep API; the
25    /// row is safe to skip at a glance.
26    pub tracked: u32,
27    pub subject: String,
28    /// The series the row was evaluated against. `None` for
29    /// pin-targeted single evaluations and pre-v7 envelopes.
30    #[serde(default)]
31    pub series: Option<SeriesName>,
32    /// Parent count of the evaluated commit; `None` for patch and PR
33    /// inputs (no commit identity) and pre-v7 envelopes.
34    #[serde(default)]
35    pub parent_count: Option<NonZeroU32>,
36    /// Paths the apply forecast predicts will conflict on the target.
37    /// Zero when the forecast is clean or was not evaluated; the
38    /// verdict kind never reflects it, so the row states it itself.
39    #[serde(default)]
40    pub apply_conflicts: u32,
41
42    /// Symbol-axis findings on the target. Zero when the axis found
43    /// nothing or was not evaluated; the verdict kind on an
44    /// all-inapplicable row never reflects it, so the row states it.
45    #[serde(default)]
46    pub target_findings: u32,
47}
48
49vocabulary!(
50    /// Kind-only projection of `Verdict`: drops the variant payloads so a
51    /// summary row stays single-line.
52    pub enum VerdictKind: "verdict kind" {
53        Compatible => "compatible",
54        RequiresAdaptation => "requires_adaptation",
55        Incompatible => "incompatible",
56        Inapplicable => "inapplicable",
57    }
58);
59
60impl From<&Verdict> for VerdictKind {
61    fn from(v: &Verdict) -> Self {
62        match v {
63            Verdict::Compatible => Self::Compatible,
64            Verdict::RequiresAdaptation { .. } => Self::RequiresAdaptation,
65            Verdict::Incompatible { .. } => Self::Incompatible,
66            Verdict::Inapplicable { .. } => Self::Inapplicable,
67        }
68    }
69}