#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
#[non_exhaustive]
pub enum EdgeKind {
Ownership,
Import,
Reference,
Flow,
}
impl EdgeKind {
pub fn participates_in_scc(self) -> bool {
matches!(self, EdgeKind::Import | EdgeKind::Reference)
}
pub fn is_cross_file(self) -> bool {
matches!(self, EdgeKind::Import)
}
pub const fn as_str(self) -> &'static str {
match self {
EdgeKind::Ownership => "ownership",
EdgeKind::Import => "import",
EdgeKind::Reference => "reference",
EdgeKind::Flow => "flow",
}
}
}
impl std::fmt::Display for EdgeKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, serde::Serialize)]
pub struct EdgeData {
pub kind: EdgeKind,
pub confidence: f32,
pub flow_kind: Option<crate::model::FlowKind>,
}
impl EdgeData {
pub fn new(kind: EdgeKind) -> Self {
Self {
kind,
confidence: 1.0,
flow_kind: None,
}
}
pub fn with_confidence(kind: EdgeKind, confidence: f32) -> Self {
Self {
kind,
confidence: confidence.clamp(0.0, 1.0),
flow_kind: None,
}
}
pub fn flow(flow_kind: crate::model::FlowKind, confidence: f32) -> Self {
Self {
kind: EdgeKind::Flow,
confidence: confidence.clamp(0.0, 1.0),
flow_kind: Some(flow_kind),
}
}
pub fn participates_in_scc(&self) -> bool {
self.kind.participates_in_scc()
}
}
impl Default for EdgeData {
fn default() -> Self {
Self::new(EdgeKind::Reference)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn edge_kind_scc_participation() {
assert!(!EdgeKind::Ownership.participates_in_scc());
assert!(EdgeKind::Import.participates_in_scc());
assert!(EdgeKind::Reference.participates_in_scc());
}
#[test]
fn edge_kind_as_str() {
assert_eq!(EdgeKind::Ownership.as_str(), "ownership");
assert_eq!(EdgeKind::Import.as_str(), "import");
assert_eq!(EdgeKind::Reference.as_str(), "reference");
assert_eq!(EdgeKind::Flow.as_str(), "flow");
}
#[test]
fn flow_edge_excluded_from_scc() {
assert!(!EdgeKind::Flow.participates_in_scc());
}
#[test]
fn flow_edge_not_cross_file() {
assert!(!EdgeKind::Flow.is_cross_file());
}
#[test]
fn edge_kind_display() {
assert_eq!(format!("{}", EdgeKind::Import), "import");
}
#[test]
fn edge_data_new_defaults_to_full_confidence() {
let edge = EdgeData::new(EdgeKind::Import);
assert_eq!(edge.kind, EdgeKind::Import);
assert_eq!(edge.confidence, 1.0);
assert!(edge.participates_in_scc());
}
#[test]
fn edge_data_with_confidence_clamps() {
let low = EdgeData::with_confidence(EdgeKind::Reference, -0.5);
assert_eq!(low.confidence, 0.0);
let high = EdgeData::with_confidence(EdgeKind::Reference, 1.5);
assert_eq!(high.confidence, 1.0);
let mid = EdgeData::with_confidence(EdgeKind::Reference, 0.75);
assert_eq!(mid.confidence, 0.75);
}
#[test]
fn edge_data_default() {
let edge: EdgeData = Default::default();
assert_eq!(edge.confidence, 1.0);
assert!(edge.participates_in_scc());
}
}