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