Skip to main content

agentic_codebase/types/
edge.rs

1//! Edge types and structures for relationships between code units.
2
3use serde::{Deserialize, Serialize};
4
5/// The type of relationship between two code units.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[repr(u8)]
8pub enum EdgeType {
9    /// Runtime invocation: source calls target.
10    Calls = 0,
11    /// Static dependency: source imports/uses target.
12    Imports = 1,
13    /// Type hierarchy: source extends/inherits target.
14    Inherits = 2,
15    /// Interface conformance: source implements target trait/interface.
16    Implements = 3,
17    /// Method override: source overrides target method.
18    Overrides = 4,
19    /// Structural containment: source contains target (module contains function).
20    Contains = 5,
21    /// Non-call reference: source references target without calling.
22    References = 6,
23    /// Test coverage: source test covers target code.
24    Tests = 7,
25    /// Documentation: source doc describes target.
26    Documents = 8,
27    /// Configuration: source configures target.
28    Configures = 9,
29    /// Hidden coupling: changes together >70% of time (from history).
30    CouplesWith = 10,
31    /// Breaking relationship: changing source historically breaks target.
32    BreaksWith = 11,
33    /// Pattern instance: source is an instance of target pattern.
34    PatternOf = 12,
35    /// Temporal: source is newer version of target.
36    VersionOf = 13,
37    /// Cross-language: source binds to target across FFI.
38    FfiBinds = 14,
39    /// Type relationship: source uses target as a type.
40    UsesType = 15,
41    /// Return type: source returns target type.
42    Returns = 16,
43    /// Parameter type: source has parameter of target type.
44    ParamType = 17,
45}
46
47impl EdgeType {
48    /// Convert from raw byte value.
49    ///
50    /// Returns `None` for values that don't correspond to a known variant.
51    pub fn from_u8(value: u8) -> Option<Self> {
52        match value {
53            0 => Some(Self::Calls),
54            1 => Some(Self::Imports),
55            2 => Some(Self::Inherits),
56            3 => Some(Self::Implements),
57            4 => Some(Self::Overrides),
58            5 => Some(Self::Contains),
59            6 => Some(Self::References),
60            7 => Some(Self::Tests),
61            8 => Some(Self::Documents),
62            9 => Some(Self::Configures),
63            10 => Some(Self::CouplesWith),
64            11 => Some(Self::BreaksWith),
65            12 => Some(Self::PatternOf),
66            13 => Some(Self::VersionOf),
67            14 => Some(Self::FfiBinds),
68            15 => Some(Self::UsesType),
69            16 => Some(Self::Returns),
70            17 => Some(Self::ParamType),
71            _ => None,
72        }
73    }
74
75    /// Returns true if this edge type indicates a dependency.
76    pub fn is_dependency(&self) -> bool {
77        matches!(
78            self,
79            Self::Calls
80                | Self::Imports
81                | Self::Inherits
82                | Self::Implements
83                | Self::UsesType
84                | Self::FfiBinds
85        )
86    }
87
88    /// Returns true if this edge is derived from history analysis.
89    pub fn is_temporal(&self) -> bool {
90        matches!(self, Self::CouplesWith | Self::BreaksWith | Self::VersionOf)
91    }
92
93    /// Returns a human-readable label for this edge type.
94    pub fn label(&self) -> &'static str {
95        match self {
96            Self::Calls => "calls",
97            Self::Imports => "imports",
98            Self::Inherits => "inherits",
99            Self::Implements => "implements",
100            Self::Overrides => "overrides",
101            Self::Contains => "contains",
102            Self::References => "references",
103            Self::Tests => "tests",
104            Self::Documents => "documents",
105            Self::Configures => "configures",
106            Self::CouplesWith => "couples_with",
107            Self::BreaksWith => "breaks_with",
108            Self::PatternOf => "pattern_of",
109            Self::VersionOf => "version_of",
110            Self::FfiBinds => "ffi_binds",
111            Self::UsesType => "uses_type",
112            Self::Returns => "returns",
113            Self::ParamType => "param_type",
114        }
115    }
116
117    /// The total number of edge type variants.
118    pub const COUNT: usize = 18;
119}
120
121impl std::fmt::Display for EdgeType {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        write!(f, "{}", self.label())
124    }
125}
126
127/// A directed relationship between two code units.
128#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
129pub struct Edge {
130    /// Source code unit ID.
131    pub source_id: u64,
132
133    /// Target code unit ID.
134    pub target_id: u64,
135
136    /// Type of relationship.
137    pub edge_type: EdgeType,
138
139    /// Relationship strength (0.0 = weak, 1.0 = strong).
140    /// For temporal edges, this is the confidence/frequency.
141    pub weight: f32,
142
143    /// When this edge was established.
144    pub created_at: u64,
145
146    /// Additional context (e.g., call site line number).
147    pub context: u32,
148}
149
150impl Edge {
151    /// Create a new edge with default weight of 1.0.
152    pub fn new(source_id: u64, target_id: u64, edge_type: EdgeType) -> Self {
153        Self {
154            source_id,
155            target_id,
156            edge_type,
157            weight: 1.0,
158            created_at: crate::types::now_micros(),
159            context: 0,
160        }
161    }
162
163    /// Set the weight (clamped to [0.0, 1.0]).
164    pub fn with_weight(mut self, weight: f32) -> Self {
165        self.weight = weight.clamp(0.0, 1.0);
166        self
167    }
168
169    /// Set the context value.
170    pub fn with_context(mut self, context: u32) -> Self {
171        self.context = context;
172        self
173    }
174
175    /// Returns true if this is a self-edge (source == target).
176    pub fn is_self_edge(&self) -> bool {
177        self.source_id == self.target_id
178    }
179}