Skip to main content

sqry_classpath/graph/
provenance.rs

1//! Provenance tracking types for classpath entries.
2//!
3//! Tracks the origin and dependency status of each JAR on the classpath,
4//! enabling FQN precedence decisions (workspace > direct > transitive) and
5//! metadata attachment to emitted graph nodes.
6
7use std::path::PathBuf;
8
9use serde::{Deserialize, Serialize};
10
11/// Tracks the origin and dependency status of a classpath entry.
12///
13/// Used during graph emission to:
14/// 1. Set `is_direct_dependency` on [`ClasspathNodeMetadata`].
15/// 2. Control ExportMap registration order (direct before transitive).
16/// 3. Attach Maven/Gradle coordinates to nodes for provenance queries.
17///
18/// [`ClasspathNodeMetadata`]: sqry_core::graph::unified::storage::ClasspathNodeMetadata
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ClasspathProvenance {
21    /// Absolute path to the JAR file.
22    pub jar_path: PathBuf,
23    /// Maven coordinates if known (e.g., `"com.google.guava:guava:33.0.0"`).
24    pub coordinates: Option<String>,
25    /// `true` if this JAR is a direct dependency of the project;
26    /// `false` if it is a transitive (indirect) dependency.
27    pub is_direct: bool,
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn test_provenance_roundtrip_json() {
36        let prov = ClasspathProvenance {
37            jar_path: PathBuf::from("/home/user/.m2/repository/guava-33.0.0.jar"),
38            coordinates: Some("com.google.guava:guava:33.0.0".to_owned()),
39            is_direct: true,
40        };
41
42        let json = serde_json::to_string(&prov).unwrap();
43        let deserialized: ClasspathProvenance = serde_json::from_str(&json).unwrap();
44        assert_eq!(deserialized.jar_path, prov.jar_path);
45        assert_eq!(deserialized.coordinates, prov.coordinates);
46        assert_eq!(deserialized.is_direct, prov.is_direct);
47    }
48
49    #[test]
50    fn test_provenance_transitive_no_coordinates() {
51        let prov = ClasspathProvenance {
52            jar_path: PathBuf::from("/tmp/some-transitive-1.0.jar"),
53            coordinates: None,
54            is_direct: false,
55        };
56
57        assert!(!prov.is_direct);
58        assert!(prov.coordinates.is_none());
59    }
60
61    #[test]
62    fn test_provenance_postcard_roundtrip() {
63        let prov = ClasspathProvenance {
64            jar_path: PathBuf::from("/repo/.gradle/caches/guava-33.jar"),
65            coordinates: Some("com.google.guava:guava:33.0.0".to_owned()),
66            is_direct: false,
67        };
68
69        let bytes = postcard::to_allocvec(&prov).unwrap();
70        let deserialized: ClasspathProvenance = postcard::from_bytes(&bytes).unwrap();
71        assert_eq!(deserialized.jar_path, prov.jar_path);
72        assert_eq!(deserialized.coordinates, prov.coordinates);
73        assert_eq!(deserialized.is_direct, prov.is_direct);
74    }
75}