Skip to main content

parallax_project/
graph.rs

1//! ProjectGraph — authoritative semantic model for migration.
2
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6/// Graph schema version (independent of PUIR).
7pub const PROJECT_GRAPH_VERSION: u32 = 1;
8
9/// Relationship / edge kinds.
10#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum GraphEdgeKind {
13    /// Calls.
14    Calls,
15    /// Imports.
16    Imports,
17    /// Inherits.
18    Inherits,
19    /// Implements.
20    Implements,
21    /// Reads.
22    Reads,
23    /// Writes.
24    Writes,
25    /// Constructs.
26    Constructs,
27    /// Returns.
28    Returns,
29    /// Throws.
30    Throws,
31    /// Depends on package.
32    DependsOn,
33    /// Tested by.
34    Tests,
35    /// Exports.
36    Exports,
37}
38
39/// Alias used in docs.
40pub type Relationship = GraphEdgeKind;
41
42/// Node kinds in the project graph.
43#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "snake_case")]
45pub enum GraphNodeKind {
46    /// Module / file unit.
47    Module,
48    /// Function.
49    Function,
50    /// Class.
51    Class,
52    /// Struct / interface type.
53    Type,
54    /// Package dependency.
55    Package,
56    /// Test.
57    Test,
58    /// Entrypoint.
59    Entrypoint,
60    /// Resource (config, asset).
61    Resource,
62    /// Configuration file.
63    Config,
64}
65
66/// A node in the project graph.
67#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct GraphNode {
69    /// Stable id.
70    pub id: String,
71    /// Kind.
72    pub kind: GraphNodeKind,
73    /// Display name.
74    pub name: String,
75    /// Owning file if any.
76    pub file: Option<String>,
77    /// Extra attributes.
78    pub attrs: IndexMap<String, serde_json::Value>,
79}
80
81/// Directed edge.
82#[derive(Clone, Debug, Serialize, Deserialize)]
83pub struct GraphEdge {
84    /// From node id.
85    pub from: String,
86    /// To node id.
87    pub to: String,
88    /// Kind.
89    pub kind: GraphEdgeKind,
90}
91
92/// File inventory entry.
93#[derive(Clone, Debug, Serialize, Deserialize)]
94pub struct ProjectFile {
95    /// Relative path.
96    pub path: String,
97    /// Role: source | test | config | resource | other
98    pub role: String,
99    /// Language id if source.
100    pub language: Option<String>,
101    /// Byte size.
102    pub bytes: u64,
103}
104
105/// Package / dependency reference.
106#[derive(Clone, Debug, Serialize, Deserialize)]
107pub struct DependencyRef {
108    /// Ecosystem (npm, crates.io, pypi, go).
109    pub ecosystem: String,
110    /// Package name.
111    pub name: String,
112    /// Version constraint if known.
113    pub version: Option<String>,
114    /// Dev dependency?
115    pub dev: bool,
116}
117
118/// Entrypoint.
119#[derive(Clone, Debug, Serialize, Deserialize)]
120pub struct Entrypoint {
121    /// Relative path.
122    pub path: String,
123    /// Kind: bin | lib | script
124    pub kind: String,
125}
126
127/// Complete project graph.
128#[derive(Clone, Debug, Serialize, Deserialize)]
129pub struct ProjectGraph {
130    /// Schema version.
131    pub version: u32,
132    /// Project name hint.
133    pub name: String,
134    /// Files.
135    pub files: Vec<ProjectFile>,
136    /// Packages / dependencies.
137    pub packages: Vec<DependencyRef>,
138    /// Entrypoints.
139    pub entrypoints: Vec<Entrypoint>,
140    /// Nodes.
141    pub nodes: IndexMap<String, GraphNode>,
142    /// Edges.
143    pub edges: Vec<GraphEdge>,
144    /// Build system hint (npm, cargo, …).
145    pub build_system: Option<String>,
146    /// Test framework hint.
147    pub test_framework: Option<String>,
148}
149
150impl ProjectGraph {
151    /// Empty graph.
152    pub fn new(name: impl Into<String>) -> Self {
153        Self {
154            version: PROJECT_GRAPH_VERSION,
155            name: name.into(),
156            files: Vec::new(),
157            packages: Vec::new(),
158            entrypoints: Vec::new(),
159            nodes: IndexMap::new(),
160            edges: Vec::new(),
161            build_system: None,
162            test_framework: None,
163        }
164    }
165
166    /// Count source files.
167    pub fn source_file_count(&self) -> usize {
168        self.files.iter().filter(|f| f.role == "source").count()
169    }
170
171    /// Count test files.
172    pub fn test_file_count(&self) -> usize {
173        self.files.iter().filter(|f| f.role == "test").count()
174    }
175}
176
177/// Alias for docs.
178pub type ProjectPackage = DependencyRef;