Skip to main content

mantra_schema/
reviews.rs

1use std::ops::Deref;
2
3use relative_path::RelativePathBuf;
4use time::OffsetDateTime;
5
6use crate::{Line, Origin, Properties, Revision, test_runs::TestCaseState};
7
8use super::requirements::ReqId;
9
10/// A simplified format to specify the UTC date and time of a review.
11///
12/// **Examples:**
13/// - `2025-04-26 10:30utc+01`
14/// - `2025-10-20T12:30:10.147utc-04`
15pub const REVIEW_DATE_FORMAT: &[time::format_description::BorrowedFormatItem<'static>] = time::macros::format_description!(
16    "[year]-[month]-[day][first [T] [ ]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]]utc[offset_hour sign:mandatory]"
17);
18
19time::serde::format_description!(pub review_date_format, OffsetDateTime, REVIEW_DATE_FORMAT);
20
21/// Tries to convert the given string to an [`OffsetDateTime`] using the [`REVIEW_DATE_FORMAT`].
22pub fn date_from_str(date: &str) -> Result<OffsetDateTime, time::error::Parse> {
23    OffsetDateTime::parse(date, REVIEW_DATE_FORMAT)
24        .or_else(|_| super::test_runs::test_date_from_str(date))
25}
26
27/// Defines the schema to exchange review related information.
28/// [req("exchange.review.schema")]
29#[derive(
30    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
31)]
32#[serde(rename_all = "snake_case", deny_unknown_fields)]
33pub struct ReviewSchema {
34    /// The schema version.
35    /// [req("exchange.versioned")]
36    #[serde(serialize_with = "crate::serialize_schema_version")]
37    pub schema_version: Option<String>,
38    pub reviews: Vec<Review>,
39    /// Optional properties related to all reviews in this entry.
40    ///
41    /// **Note:** If a review sets a property key directly,
42    /// the value set at the review will be taken.
43    pub properties: Option<Properties>,
44    /// Optional base origin of the reviews in this entry.
45    /// e.g. specific branch or commit from a git repository
46    pub origin: Option<Origin>,
47}
48
49/// Defines the fields for a review.
50/// [req("exchange.review")]
51#[derive(
52    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
53)]
54#[serde(rename_all = "snake_case", deny_unknown_fields)]
55pub struct Review {
56    /// The name of the review.
57    /// [req("review.id")]
58    pub name: String,
59    /// The UTC date and time the review was started.
60    /// [req("review.id")]
61    #[serde(with = "review_date_format")]
62    #[schemars(
63        with = "String",
64        regex(
65            pattern = r"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})(T| )(?<hour>\d{2}):(?<minute>\d{2})(?<second>:\d{2}(?<subsecond>\.\d{3})?)?utc(?<utc>(-|+)\d{2})"
66        )
67    )]
68    pub utc_date: OffsetDateTime,
69    /// The authors that were part of the review.
70    /// [req("review.authors")]
71    pub authors: Vec<String>,
72    /// Optional description of the review.
73    /// [req("review.description")]
74    pub description: Option<String>,
75    /// Optional origin of the review.
76    /// [req("review.origin")]
77    pub origin: Option<Origin>,
78    /// Optional properties related to this review.
79    pub properties: Option<Properties>,
80    /// Optional revisions for the review.
81    pub revisions: Option<Vec<Revision>>,
82    /// List of requirements that are verified in this review.
83    /// [req("review.verify_req")]
84    #[serde(alias = "requirement", default)]
85    pub requirements: Vec<VerifiedRequirementGroup>,
86    /// List of test run overrides added with this review.
87    /// [req("review.test_case_state", "review.coverage")]
88    #[serde(alias = "override", default)]
89    pub test_run_overrides: Vec<OverrideTestRun>,
90}
91
92/// Represents a verification entry affecting one or more requirements.
93/// [req("review.verify_req")]
94#[derive(
95    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
96)]
97#[serde(deny_unknown_fields)]
98pub struct VerifiedRequirementGroup {
99    /// One or more requirement IDs to mark the related requirements as manually verified by a review.
100    #[serde(alias = "ids")]
101    pub id: OneOrMultRequirementIds,
102    /// Mandatory comment explaining the manual verification.
103    pub comment: String,
104}
105
106/// Variant to allow setting either one or more requirement IDs for manual verification.
107#[derive(
108    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
109)]
110#[serde(untagged)]
111pub enum OneOrMultRequirementIds {
112    /// Only one requirement is verified by a verification entry.
113    One(ReqId),
114    /// List of requirements that are all verified by one verification entry.
115    Mult(Vec<ReqId>),
116}
117
118impl std::fmt::Display for OneOrMultRequirementIds {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        match &self {
121            OneOrMultRequirementIds::One(req_id) => write!(f, "{}", req_id),
122            OneOrMultRequirementIds::Mult(req_ids) => {
123                write!(
124                    f,
125                    "{}",
126                    req_ids
127                        .iter()
128                        .map(|r| r.deref().clone())
129                        .collect::<Vec<String>>()
130                        .join(",")
131                )
132            }
133        }
134    }
135}
136
137/// Represents review overrides for a specific test run.
138/// [req("review.test_case_state", "review.coverage")]
139#[derive(
140    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
141)]
142#[serde(rename_all = "snake_case", deny_unknown_fields)]
143pub struct OverrideTestRun {
144    /// Name of the test run.
145    pub name: String,
146    /// The UTC date the test run execution started.
147    #[serde(with = "time::serde::iso8601")]
148    #[schemars(with = "String")]
149    pub utc_date: time::OffsetDateTime,
150    /// List of test case state overrides.
151    /// [req("review.test_case_state")]
152    #[serde(alias = "test", default)]
153    pub test_cases: Vec<OverrideTestCase>,
154    /// List of file coverage overrides.
155    /// [req("review.coverage")]
156    #[serde(default)]
157    pub coverage: Vec<OverrideFileCoverage>,
158}
159
160/// Represents a test case state override in a review.
161/// [req("review.test_case_state", "review.coverage")]
162#[derive(
163    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
164)]
165#[serde(deny_unknown_fields)]
166pub struct OverrideTestCase {
167    /// Name of the test case whose state and/or related code coverage is overridden in a review.
168    pub name: String,
169    /// Overrides the state of the test case.
170    /// [req("review.test_case_state")]
171    pub state: Option<OverrideTestCaseState>,
172    /// Overrides the code coverage related to this test case.
173    /// [req("review.coverage")]
174    #[serde(default)]
175    pub coverage: Vec<OverrideFileCoverage>,
176}
177
178/// Represents a test case state override in a review.
179/// [req("review.test_case_state")]
180#[derive(
181    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
182)]
183#[serde(deny_unknown_fields)]
184pub struct OverrideTestCaseState {
185    /// The new state that is set with this override.
186    pub new: TestCaseState,
187    /// Mandatory comment explaining why the state is overriden via review.
188    pub comment: String,
189}
190
191/// Code coverage overrides per file.
192/// [req("review.coverage")]
193#[derive(
194    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
195)]
196#[serde(deny_unknown_fields)]
197pub struct OverrideFileCoverage {
198    /// The file whose coverage data is overridden in a review.
199    #[schemars(with = "String")]
200    pub filepath: RelativePathBuf,
201    /// The line information in the file that is overridden.
202    #[serde(default)]
203    pub lines: Vec<OverrideCoveredLineInfo>,
204}
205
206/// Code coverage override of one or more lines.
207/// [req("review.coverage")]
208#[derive(
209    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
210)]
211#[serde(deny_unknown_fields)]
212pub struct OverrideCoveredLineInfo {
213    /// The number of lines affected by this override.
214    #[serde(alias = "nr")]
215    pub nrs: Vec<Line>,
216    /// The new number of times the set lines are reached during a test run or test case execution.
217    /// If None, the line is marked to be ignored from line coverage analysis.
218    pub hits: Option<i64>,
219    /// Mandatory comment explaining the change to the code coverage information.
220    pub comment: String,
221}