Skip to main content

code_ranker_graph/
snapshot.rs

1//! The serializable analysis artifact ([`Snapshot`]) and its header types
2//! ([`GitInfo`], [`StageTime`]).
3//!
4//! Shape (schema version `"2"`): the snapshot keeps the historical header
5//! (workspace/target/plugin/roots/versions/git/timings) and carries a `graphs`
6//! map `level_name -> LevelGraph`. The per-level payload lives in
7//! [`crate::level_graph`]; canonical serialization in [`crate::serialize`]; id
8//! relativization in [`crate::relativize`].
9
10use crate::level_graph::LevelGraph;
11use chrono::{DateTime, Utc};
12use code_ranker_plugin_api::plugin::Preset;
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15
16/// The snapshot schema version this build produces and can read back. A
17/// `--baseline` (or snapshot input) with a different version is rejected with a
18/// structured error rather than silently mis-parsed.
19pub const SCHEMA_VERSION: &str = "2";
20
21/// Per-stage timing in milliseconds, in execution order.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct StageTime {
24    pub stage: String,
25    pub ms: u64,
26    #[serde(default, skip_serializing_if = "String::is_empty")]
27    pub detail: String,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Snapshot {
32    pub schema_version: String,
33    pub generated_at: DateTime<Utc>,
34    pub command: String,
35    /// Directory from which `code-ranker` was invoked.
36    pub workspace: String,
37    /// The analyzed project directory (absolute path, stored once here).
38    pub target: String,
39    pub plugin: String,
40    /// Config file used for this analysis, if any was found.
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub config_file: Option<String>,
43    pub versions: BTreeMap<String, String>,
44    /// Named system roots used to shorten node paths (e.g. `{registry}`).
45    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
46    pub roots: BTreeMap<String, String>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub git: Option<GitInfo>,
49    #[serde(default, skip_serializing_if = "Vec::is_empty")]
50    pub timings: Vec<StageTime>,
51    /// Analysis levels, keyed by level name. Today only `"files"` is produced.
52    pub graphs: BTreeMap<String, LevelGraph>,
53    /// Prompt-Generator presets (refactoring principles), language-adapted.
54    #[serde(default, skip_serializing_if = "Vec::is_empty")]
55    pub presets: Vec<Preset>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct GitInfo {
60    pub branch: String,
61    pub commit: String,
62    pub dirty_files: u32,
63    /// Remote `origin` URL (raw). Used by the HTML report for source links.
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub origin: Option<String>,
66}
67
68impl Snapshot {
69    #[allow(clippy::too_many_arguments)]
70    pub fn new(
71        command: String,
72        workspace: String,
73        target: String,
74        plugin: String,
75        config_file: Option<String>,
76        versions: BTreeMap<String, String>,
77        roots: BTreeMap<String, String>,
78        git: Option<GitInfo>,
79        timings: Vec<StageTime>,
80        graphs: BTreeMap<String, LevelGraph>,
81        presets: Vec<Preset>,
82    ) -> Self {
83        Self {
84            schema_version: SCHEMA_VERSION.to_string(),
85            generated_at: Utc::now(),
86            command,
87            workspace,
88            target,
89            plugin,
90            config_file,
91            versions,
92            roots,
93            git,
94            timings,
95            graphs,
96            presets,
97        }
98    }
99}