Skip to main content

remendo/models/
common.rs

1//! Shared types used across multiple model modules.
2
3use serde::Deserialize;
4use std::fmt;
5
6/// Severity level for a review finding.
7///
8/// Ordered from least to most severe: `Low < Medium < High < Critical`.
9///
10/// # Examples
11///
12/// ```
13/// use remendo::models::Severity;
14///
15/// let low = Severity::Low;
16/// let critical = Severity::Critical;
17/// assert!(low < critical);
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum Severity {
22    /// Minor issue, informational.
23    Low = 1,
24    /// Moderate concern worth investigating.
25    Medium = 2,
26    /// Significant issue likely to cause problems.
27    High = 3,
28    /// Severe issue requiring immediate attention.
29    Critical = 4,
30}
31
32impl fmt::Display for Severity {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Self::Low => write!(f, "Low"),
36            Self::Medium => write!(f, "Medium"),
37            Self::High => write!(f, "High"),
38            Self::Critical => write!(f, "Critical"),
39        }
40    }
41}
42
43/// Git baseline reference for a patchset.
44#[derive(Debug, Clone, PartialEq, Deserialize)]
45pub struct Baseline {
46    /// Branch name (e.g., `"nf/HEAD"`).
47    #[serde(default)]
48    pub branch: Option<String>,
49    /// Commit hash.
50    #[serde(default)]
51    pub commit: Option<String>,
52    /// Repository URL.
53    #[serde(default)]
54    pub repo_url: Option<String>,
55}
56
57/// A tracked mailing list from the Sashiko instance.
58#[derive(Debug, Clone, PartialEq, Deserialize)]
59pub struct MailingList {
60    /// Human-readable name of the mailing list.
61    pub name: String,
62    /// NNTP group identifier.
63    #[serde(default)]
64    pub group: Option<String>,
65}
66
67/// Server health and queue statistics from `/api/stats`.
68#[derive(Debug, Clone, PartialEq, Deserialize)]
69pub struct ServerStats {
70    /// Server status (typically `"ok"`).
71    pub status: String,
72    /// Sashiko version string.
73    pub version: String,
74    /// Number of patchsets pending review.
75    #[serde(default)]
76    pub pending: u64,
77    /// Number of patchsets currently being reviewed.
78    #[serde(default)]
79    pub reviewing: u64,
80    /// Total ingested messages.
81    #[serde(default)]
82    pub messages: u64,
83    /// Total ingested patchsets.
84    #[serde(default)]
85    pub patchsets: u64,
86}
87
88/// Identifies a patchset, patch, or message by numeric ID or RFC 2822 Message-ID.
89///
90/// The Sashiko API accepts both forms for lookup endpoints.
91#[derive(Debug, Clone, PartialEq, Eq, Hash)]
92pub enum PatchId {
93    /// Database numeric identifier.
94    Numeric(i64),
95    /// RFC 2822 `Message-ID` string.
96    MessageId(String),
97}
98
99impl fmt::Display for PatchId {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        match self {
102            Self::Numeric(id) => write!(f, "{id}"),
103            Self::MessageId(mid) => write!(f, "{mid}"),
104        }
105    }
106}
107
108#[cfg(test)]
109#[allow(clippy::expect_used)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn severity_ordering() {
115        let mut severities = vec![
116            Severity::Critical,
117            Severity::Low,
118            Severity::High,
119            Severity::Medium,
120        ];
121        severities.sort();
122        assert_eq!(
123            severities,
124            vec![
125                Severity::Low,
126                Severity::Medium,
127                Severity::High,
128                Severity::Critical,
129            ]
130        );
131    }
132
133    #[test]
134    fn severity_display() {
135        assert_eq!(Severity::Low.to_string(), "Low");
136        assert_eq!(Severity::Critical.to_string(), "Critical");
137    }
138
139    #[test]
140    fn patch_id_display() {
141        let numeric = PatchId::Numeric(42);
142        assert_eq!(numeric.to_string(), "42");
143
144        let msg = PatchId::MessageId("20260513-foo@example.com".to_string());
145        assert_eq!(msg.to_string(), "20260513-foo@example.com");
146    }
147}