sqry_classpath/graph/
provenance.rs1use std::path::PathBuf;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ClasspathScope {
14 pub module_name: String,
16 pub module_root: PathBuf,
18 pub is_direct: bool,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ClasspathProvenance {
32 pub jar_path: PathBuf,
34 pub coordinates: Option<String>,
36 pub is_direct: bool,
42 #[serde(default)]
44 pub scopes: Vec<ClasspathScope>,
45}
46
47impl ClasspathProvenance {
48 #[must_use]
50 pub fn has_direct_scope(&self) -> bool {
51 self.scopes.iter().any(|scope| scope.is_direct)
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_provenance_roundtrip_json() {
61 let prov = ClasspathProvenance {
62 jar_path: PathBuf::from("/home/user/.m2/repository/guava-33.0.0.jar"),
63 coordinates: Some("com.google.guava:guava:33.0.0".to_owned()),
64 is_direct: true,
65 scopes: vec![ClasspathScope {
66 module_name: "app".to_owned(),
67 module_root: PathBuf::from("/repo/app"),
68 is_direct: true,
69 }],
70 };
71
72 let json = serde_json::to_string(&prov).unwrap();
73 let deserialized: ClasspathProvenance = serde_json::from_str(&json).unwrap();
74 assert_eq!(deserialized.jar_path, prov.jar_path);
75 assert_eq!(deserialized.coordinates, prov.coordinates);
76 assert_eq!(deserialized.is_direct, prov.is_direct);
77 }
78
79 #[test]
80 fn test_provenance_transitive_no_coordinates() {
81 let prov = ClasspathProvenance {
82 jar_path: PathBuf::from("/tmp/some-transitive-1.0.jar"),
83 coordinates: None,
84 is_direct: false,
85 scopes: vec![ClasspathScope {
86 module_name: "lib".to_owned(),
87 module_root: PathBuf::from("/repo/lib"),
88 is_direct: false,
89 }],
90 };
91
92 assert!(!prov.is_direct);
93 assert!(prov.coordinates.is_none());
94 }
95
96 #[test]
97 fn test_provenance_postcard_roundtrip() {
98 let prov = ClasspathProvenance {
99 jar_path: PathBuf::from("/repo/.gradle/caches/guava-33.jar"),
100 coordinates: Some("com.google.guava:guava:33.0.0".to_owned()),
101 is_direct: false,
102 scopes: vec![ClasspathScope {
103 module_name: "app".to_owned(),
104 module_root: PathBuf::from("/repo/app"),
105 is_direct: false,
106 }],
107 };
108
109 let bytes = postcard::to_allocvec(&prov).unwrap();
110 let deserialized: ClasspathProvenance = postcard::from_bytes(&bytes).unwrap();
111 assert_eq!(deserialized.jar_path, prov.jar_path);
112 assert_eq!(deserialized.coordinates, prov.coordinates);
113 assert_eq!(deserialized.is_direct, prov.is_direct);
114 }
115}