Skip to main content

lean_ctx/core/property_graph/
meta.rs

1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5#[serde(default)]
6pub struct PropertyGraphMetaV1 {
7    pub schema_version: u32,
8    /// Property-graph engine generation that produced this graph (see
9    /// [`super::GRAPH_ENGINE_VERSION`]). Bumped whenever edge extraction
10    /// changes, so a graph built by an older engine — e.g. before the C#/Java
11    /// `type_ref` edges existed (GH #398) — is rebuilt instead of silently
12    /// served without the new edges. Graphs written before this field existed
13    /// deserialize to `0`.
14    pub engine_version: u32,
15    /// lean-ctx version (`CARGO_PKG_VERSION`) that built this graph, recorded
16    /// for diagnostics. Empty for graphs written before the stamp existed.
17    pub built_with: String,
18    /// Absolute, normalized project root this graph was built for. Recorded so
19    /// the one-way `graphs/<hash>/` directory can be pruned when its project no
20    /// longer exists on disk (#696 C4 — replaces the `project_root` the retired
21    /// JSON index used to carry). Empty for graphs written before this field.
22    pub project_root: String,
23    /// RFC3339 timestamp (UTC) of the last successful build.
24    pub built_at: String,
25    /// Git HEAD (short) at build time, if available.
26    pub git_head: Option<String>,
27    /// Git dirty flag at build time, if available.
28    pub git_dirty: Option<bool>,
29    /// Node count after build.
30    pub nodes: Option<usize>,
31    /// Edge count after build.
32    pub edges: Option<usize>,
33    /// Number of source files processed during build (before filtering).
34    pub files_indexed: Option<usize>,
35    /// Build duration in milliseconds (best-effort).
36    pub build_time_ms: Option<u64>,
37}
38
39impl Default for PropertyGraphMetaV1 {
40    fn default() -> Self {
41        Self {
42            schema_version: 1,
43            engine_version: 0,
44            built_with: String::new(),
45            project_root: String::new(),
46            built_at: String::new(),
47            git_head: None,
48            git_dirty: None,
49            nodes: None,
50            edges: None,
51            files_indexed: None,
52            build_time_ms: None,
53        }
54    }
55}
56
57pub fn meta_path(project_root: &str) -> PathBuf {
58    super::graph_dir(project_root).join("graph.meta.json")
59}
60
61pub fn load_meta(project_root: &str) -> Option<PropertyGraphMetaV1> {
62    let path = meta_path(project_root);
63    let s = std::fs::read_to_string(path).ok()?;
64    let meta: PropertyGraphMetaV1 = serde_json::from_str(&s).ok()?;
65    if meta.schema_version != 1 || meta.built_at.trim().is_empty() {
66        return None;
67    }
68    Some(meta)
69}
70
71pub fn write_meta(project_root: &str, meta: &PropertyGraphMetaV1) -> Result<PathBuf, String> {
72    let path = meta_path(project_root);
73    if let Some(parent) = path.parent() {
74        std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
75    }
76    let json = serde_json::to_string_pretty(meta).map_err(|e| e.to_string())?;
77    crate::config_io::write_atomic(&path, &json)?;
78    Ok(path)
79}