Skip to main content

sinter_core/
edge.rs

1use serde::{Deserialize, Serialize};
2
3use crate::node::{NodeId, Span};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
6pub enum Relation {
7    Calls,
8    Uses,
9    Imports,
10    Contains,
11    Implements,
12    Extends,
13    /// The source reads rows from the destination relation.
14    Reads,
15    /// The source can insert, update, or delete rows in the destination table.
16    Writes,
17    /// The source declares creation of the destination database object.
18    Creates,
19    /// The source changes the destination database object.
20    Alters,
21    /// The source declares removal of the destination database object.
22    Drops,
23}
24
25impl Relation {
26    pub fn as_str(self) -> &'static str {
27        match self {
28            Self::Calls => "calls",
29            Self::Uses => "uses",
30            Self::Imports => "imports",
31            Self::Contains => "contains",
32            Self::Implements => "implements",
33            Self::Extends => "extends",
34            Self::Reads => "reads",
35            Self::Writes => "writes",
36            Self::Creates => "creates",
37            Self::Alters => "alters",
38            Self::Drops => "drops",
39        }
40    }
41}
42
43/// What binds this edge to its target (R2: evidence or nothing).
44/// Global name uniqueness is not evidence and has no variant here.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
46pub enum Evidence {
47    /// Syntactic containment seen directly in the parse tree.
48    Structural,
49    /// Name visible in the reference's own file scope.
50    Scope,
51    /// An import statement binds the reference's path to the target.
52    Import,
53    /// A compiler-produced SCIP index binds reference to definition.
54    Scip,
55    /// An operator-declared binding from a workspace manifest (runtime
56    /// coupling like queue topics/HTTP routes that no static analysis can
57    /// see). Auditable in the manifest; never inferred.
58    Declared,
59    /// Dynamic-dispatch fan-out: a trait/interface method is assumed to
60    /// reach every implementation in the corpus. Conservative
61    /// over-approximation, deliberately excludable (`--certain`,
62    /// `--evidence` without "dynamic").
63    Dynamic,
64}
65
66impl Evidence {
67    pub fn as_str(self) -> &'static str {
68        match self {
69            Self::Structural => "structural",
70            Self::Scope => "scope",
71            Self::Import => "import",
72            Self::Scip => "scip",
73            Self::Declared => "declared",
74            Self::Dynamic => "dynamic",
75        }
76    }
77
78    /// Compiler-grade evidence is certain; heuristic-free but indirect
79    /// evidence (scope/import matching) is inferred.
80    pub fn confidence(self) -> Confidence {
81        match self {
82            Self::Structural | Self::Scip | Self::Declared => Confidence::Certain,
83            Self::Scope | Self::Import | Self::Dynamic => Confidence::Inferred,
84        }
85    }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
89pub enum Confidence {
90    Certain,
91    Inferred,
92}
93
94/// Directed edge `src -> dst`. The graph is a multigraph: parallel edges
95/// that differ in relation, evidence, or confidence coexist.
96#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
97pub struct Edge {
98    pub src: NodeId,
99    pub dst: NodeId,
100    pub relation: Relation,
101    pub evidence: Evidence,
102    pub confidence: Confidence,
103    /// Byte span of the binding reference in the src node's file (the file
104    /// is derivable from `src`). None when no single site exists:
105    /// containment, dynamic fan-out, implements/extends pairing, declared
106    /// links. When several sites bind the same (src, dst, relation,
107    /// evidence), storage keeps ONE representative site — the field is
108    /// last so identity orders before site.
109    pub site: Option<Span>,
110}
111
112impl Edge {
113    /// Identity without the site: two edges equal here are the same
114    /// dependency fact observed at (possibly) different call sites.
115    pub fn identity(&self) -> (&NodeId, &NodeId, Relation, Evidence, Confidence) {
116        (
117            &self.src,
118            &self.dst,
119            self.relation,
120            self.evidence,
121            self.confidence,
122        )
123    }
124}