use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; /// Type of code node in the knowledge graph #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum CodeNodeKind { Function, Method, Class, Struct, Enum, Interface, Trait, Module, File, } impl std::fmt::Display for CodeNodeKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Function => write!(f, "function"), Self::Method => write!(f, "method"), Self::Class => write!(f, "class"), Self::Struct => write!(f, "struct"), Self::Enum => write!(f, "enum"), Self::Interface => write!(f, "interface"), Self::Trait => write!(f, "trait"), Self::Module => write!(f, "module"), Self::File => write!(f, "file"), } } } /// A node in the code knowledge graph #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CodeNode { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub repo_id: String, pub graph_build_id: String, /// Unique identifier within the graph (e.g., "src/main.rs::main") pub qualified_name: String, pub name: String, pub kind: CodeNodeKind, pub file_path: String, pub start_line: u32, pub end_line: u32, /// Language of the source file pub language: String, /// Community ID from Louvain clustering pub community_id: Option, /// Whether this is a public entry point (main, exported fn, HTTP handler, etc.) pub is_entry_point: bool, /// Internal petgraph node index for fast lookups #[serde(skip_serializing_if = "Option::is_none")] pub graph_index: Option, } /// Type of relationship between code nodes #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum CodeEdgeKind { Calls, Imports, Inherits, Implements, Contains, /// A type reference (e.g., function parameter type, return type) TypeRef, } impl std::fmt::Display for CodeEdgeKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Calls => write!(f, "calls"), Self::Imports => write!(f, "imports"), Self::Inherits => write!(f, "inherits"), Self::Implements => write!(f, "implements"), Self::Contains => write!(f, "contains"), Self::TypeRef => write!(f, "type_ref"), } } } /// An edge in the code knowledge graph #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CodeEdge { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub repo_id: String, pub graph_build_id: String, /// Qualified name of source node pub source: String, /// Qualified name of target node pub target: String, pub kind: CodeEdgeKind, /// File where this relationship was found pub file_path: String, pub line_number: Option, } /// Status of a graph build operation #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum GraphBuildStatus { Running, Completed, Failed, } /// Tracks a graph build operation for a repo/commit #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GraphBuildRun { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub repo_id: String, pub commit_sha: Option, pub status: GraphBuildStatus, pub node_count: u32, pub edge_count: u32, pub community_count: u32, pub languages_parsed: Vec, pub error_message: Option, #[serde(with = "super::serde_helpers::bson_datetime")] pub started_at: DateTime, #[serde(default, with = "super::serde_helpers::opt_bson_datetime")] pub completed_at: Option>, } impl GraphBuildRun { pub fn new(repo_id: String) -> Self { Self { id: None, repo_id, commit_sha: None, status: GraphBuildStatus::Running, node_count: 0, edge_count: 0, community_count: 0, languages_parsed: Vec::new(), error_message: None, started_at: Utc::now(), completed_at: None, } } } /// Impact analysis result for a finding #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ImpactAnalysis { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub repo_id: String, pub finding_id: String, pub graph_build_id: String, /// Number of nodes reachable from the finding location pub blast_radius: u32, /// Entry points affected by this finding (via reverse call chain) pub affected_entry_points: Vec, /// Call chains from entry points to the finding location pub call_chains: Vec>, /// Community IDs affected pub affected_communities: Vec, /// Direct callers of the affected function pub direct_callers: Vec, /// Direct callees of the affected function pub direct_callees: Vec, #[serde(with = "super::serde_helpers::bson_datetime")] pub created_at: DateTime, } impl ImpactAnalysis { pub fn new(repo_id: String, finding_id: String, graph_build_id: String) -> Self { Self { id: None, repo_id, finding_id, graph_build_id, blast_radius: 0, affected_entry_points: Vec::new(), call_chains: Vec::new(), affected_communities: Vec::new(), direct_callers: Vec::new(), direct_callees: Vec::new(), created_at: Utc::now(), } } }