1use std::fmt;
4use std::sync::Arc;
5
6use serde::Serialize;
7
8use super::module_boundary::ModuleBoundaryInputError;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
12#[serde(rename_all = "lowercase")]
13pub enum GateSeverity {
14 Deny,
16 Warn,
18 Info,
20}
21
22impl fmt::Display for GateSeverity {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::Deny => f.write_str("deny"),
26 Self::Warn => f.write_str("warn"),
27 Self::Info => f.write_str("info"),
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum GateAnchor {
39 Span,
41 File,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
50pub struct GateLocation {
51 path: Arc<str>,
52 line: u32,
53 column: u32,
54 #[serde(skip)]
55 anchor: GateAnchor,
56}
57
58impl GateLocation {
59 pub fn try_new(
61 path: impl Into<Arc<str>>,
62 line: u32,
63 column: u32,
64 ) -> Result<Self, ModuleBoundaryInputError> {
65 Self::anchored(path, line, column, GateAnchor::Span)
66 }
67
68 pub fn try_new_file(path: impl Into<Arc<str>>) -> Result<Self, ModuleBoundaryInputError> {
70 Self::anchored(path, 1, 1, GateAnchor::File)
71 }
72
73 fn anchored(
74 path: impl Into<Arc<str>>,
75 line: u32,
76 column: u32,
77 anchor: GateAnchor,
78 ) -> Result<Self, ModuleBoundaryInputError> {
79 let path = path.into();
80 match (path.is_empty(), line, column) {
81 (true, _, _) => Err(ModuleBoundaryInputError::EmptyLocationPath),
82 (false, 0, _) => Err(ModuleBoundaryInputError::ZeroLocationLine),
83 (false, _, 0) => Err(ModuleBoundaryInputError::ZeroLocationColumn),
84 (false, _, _) => Ok(Self {
85 path,
86 line,
87 column,
88 anchor,
89 }),
90 }
91 }
92
93 pub fn path(&self) -> &str {
95 &self.path
96 }
97
98 pub fn line(&self) -> u32 {
100 self.line
101 }
102
103 pub fn column(&self) -> u32 {
105 self.column
106 }
107
108 pub fn anchor(&self) -> GateAnchor {
110 self.anchor
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
120pub struct ModuleBoundaryEntity {
121 ordinal: u32,
122 label: Arc<str>,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 location: Option<GateLocation>,
125}
126
127impl ModuleBoundaryEntity {
128 pub fn try_new(
130 ordinal: u32,
131 label: impl Into<Arc<str>>,
132 location: Option<GateLocation>,
133 ) -> Result<Self, ModuleBoundaryInputError> {
134 let label = label.into();
135 match label.is_empty() {
136 true => Err(ModuleBoundaryInputError::EmptyEntityLabel { ordinal }),
137 false => Ok(Self {
138 ordinal,
139 label,
140 location,
141 }),
142 }
143 }
144
145 pub fn ordinal(&self) -> u32 {
147 self.ordinal
148 }
149
150 pub fn label(&self) -> &str {
152 &self.label
153 }
154
155 pub fn location(&self) -> Option<&GateLocation> {
157 self.location.as_ref()
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
163#[serde(tag = "kind", rename_all = "snake_case")]
164pub enum ModuleBoundaryMeasurement {
165 Component {
167 cyclic: bool,
169 members: Arc<[ModuleBoundaryEntity]>,
171 partitions: Arc<[ModuleBoundaryEntity]>,
173 },
174 MisplacedSymbol {
176 declared_partition: ModuleBoundaryEntity,
178 candidate_partition: ModuleBoundaryEntity,
180 foreign_edges: u32,
182 total_outgoing_edges: u32,
184 },
185 LowModuleCohesion {
187 internal_edges: u32,
189 boundary_edges: u32,
191 },
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
196pub struct ModuleBoundaryEvidence {
197 target: Arc<str>,
198 subject: ModuleBoundaryEntity,
199 measurement: ModuleBoundaryMeasurement,
200}
201
202impl ModuleBoundaryEvidence {
203 pub(super) fn new(
205 target: Arc<str>,
206 subject: ModuleBoundaryEntity,
207 measurement: ModuleBoundaryMeasurement,
208 ) -> Self {
209 Self {
210 target,
211 subject,
212 measurement,
213 }
214 }
215
216 pub fn target(&self) -> &str {
218 &self.target
219 }
220
221 pub fn subject(&self) -> &ModuleBoundaryEntity {
223 &self.subject
224 }
225
226 pub fn measurement(&self) -> &ModuleBoundaryMeasurement {
228 &self.measurement
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
234#[serde(tag = "domain", content = "evidence", rename_all = "snake_case")]
235pub enum GateEvidence {
236 ModuleBoundary(ModuleBoundaryEvidence),
238}
239
240#[derive(Debug, Serialize)]
242pub struct GateVerdict {
243 pub rule: &'static str,
245 pub severity: GateSeverity,
247 pub rationale: &'static str,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub evidence: Option<GateEvidence>,
252}