use std::fmt;
use std::sync::Arc;
use serde::Serialize;
use super::module_boundary::ModuleBoundaryInputError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum GateSeverity {
Deny,
Warn,
Info,
}
impl fmt::Display for GateSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Deny => f.write_str("deny"),
Self::Warn => f.write_str("warn"),
Self::Info => f.write_str("info"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GateAnchor {
Span,
File,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct GateLocation {
path: Arc<str>,
line: u32,
column: u32,
#[serde(skip)]
anchor: GateAnchor,
}
impl GateLocation {
pub fn try_new(
path: impl Into<Arc<str>>,
line: u32,
column: u32,
) -> Result<Self, ModuleBoundaryInputError> {
Self::anchored(path, line, column, GateAnchor::Span)
}
pub fn try_new_file(path: impl Into<Arc<str>>) -> Result<Self, ModuleBoundaryInputError> {
Self::anchored(path, 1, 1, GateAnchor::File)
}
fn anchored(
path: impl Into<Arc<str>>,
line: u32,
column: u32,
anchor: GateAnchor,
) -> Result<Self, ModuleBoundaryInputError> {
let path = path.into();
match (path.is_empty(), line, column) {
(true, _, _) => Err(ModuleBoundaryInputError::EmptyLocationPath),
(false, 0, _) => Err(ModuleBoundaryInputError::ZeroLocationLine),
(false, _, 0) => Err(ModuleBoundaryInputError::ZeroLocationColumn),
(false, _, _) => Ok(Self {
path,
line,
column,
anchor,
}),
}
}
pub fn path(&self) -> &str {
&self.path
}
pub fn line(&self) -> u32 {
self.line
}
pub fn column(&self) -> u32 {
self.column
}
pub fn anchor(&self) -> GateAnchor {
self.anchor
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ModuleBoundaryEntity {
ordinal: u32,
label: Arc<str>,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<GateLocation>,
}
impl ModuleBoundaryEntity {
pub fn try_new(
ordinal: u32,
label: impl Into<Arc<str>>,
location: Option<GateLocation>,
) -> Result<Self, ModuleBoundaryInputError> {
let label = label.into();
match label.is_empty() {
true => Err(ModuleBoundaryInputError::EmptyEntityLabel { ordinal }),
false => Ok(Self {
ordinal,
label,
location,
}),
}
}
pub fn ordinal(&self) -> u32 {
self.ordinal
}
pub fn label(&self) -> &str {
&self.label
}
pub fn location(&self) -> Option<&GateLocation> {
self.location.as_ref()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ModuleBoundaryMeasurement {
Component {
cyclic: bool,
members: Arc<[ModuleBoundaryEntity]>,
partitions: Arc<[ModuleBoundaryEntity]>,
},
MisplacedSymbol {
declared_partition: ModuleBoundaryEntity,
candidate_partition: ModuleBoundaryEntity,
foreign_edges: u32,
total_outgoing_edges: u32,
},
LowModuleCohesion {
internal_edges: u32,
boundary_edges: u32,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ModuleBoundaryEvidence {
target: Arc<str>,
subject: ModuleBoundaryEntity,
measurement: ModuleBoundaryMeasurement,
}
impl ModuleBoundaryEvidence {
pub(super) fn new(
target: Arc<str>,
subject: ModuleBoundaryEntity,
measurement: ModuleBoundaryMeasurement,
) -> Self {
Self {
target,
subject,
measurement,
}
}
pub fn target(&self) -> &str {
&self.target
}
pub fn subject(&self) -> &ModuleBoundaryEntity {
&self.subject
}
pub fn measurement(&self) -> &ModuleBoundaryMeasurement {
&self.measurement
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "domain", content = "evidence", rename_all = "snake_case")]
pub enum GateEvidence {
ModuleBoundary(ModuleBoundaryEvidence),
}
#[derive(Debug, Serialize)]
pub struct GateVerdict {
pub rule: &'static str,
pub severity: GateSeverity,
pub rationale: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub evidence: Option<GateEvidence>,
}