actr_cli/core/components/
fingerprint_validator.rs1use anyhow::Result;
4use async_trait::async_trait;
5use sha2::{Digest, Sha256};
6use std::io::Read;
7use std::path::Path;
8use walkdir::WalkDir;
9
10use super::{Fingerprint, FingerprintValidator, ResolvedDependency, ServiceInfo};
11
12pub struct DefaultFingerprintValidator;
14
15impl DefaultFingerprintValidator {
16 pub fn new() -> Self {
17 Self
18 }
19
20 fn hash_file(path: &Path) -> Result<Vec<u8>> {
22 let mut hasher = Sha256::new();
23 let mut file = std::fs::File::open(path)?;
24 let mut buffer = [0u8; 8192];
25
26 loop {
27 let count = file.read(&mut buffer)?;
28 if count == 0 {
29 break;
30 }
31 hasher.update(&buffer[..count]);
32 }
33
34 Ok(hasher.finalize().to_vec())
35 }
36}
37
38impl Default for DefaultFingerprintValidator {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44#[async_trait]
45impl FingerprintValidator for DefaultFingerprintValidator {
46 async fn compute_service_fingerprint(&self, service: &ServiceInfo) -> Result<Fingerprint> {
47 Ok(Fingerprint {
48 algorithm: "sha256".to_string(),
49 value: service.fingerprint.clone(),
50 })
51 }
52
53 async fn verify_fingerprint(
54 &self,
55 expected: &Fingerprint,
56 actual: &Fingerprint,
57 ) -> Result<bool> {
58 Ok(expected.algorithm == actual.algorithm && expected.value == actual.value)
59 }
60
61 async fn compute_project_fingerprint(&self, project_path: &Path) -> Result<Fingerprint> {
62 let mut hasher = Sha256::new();
63 let mut proto_files: Vec<_> = WalkDir::new(project_path)
64 .into_iter()
65 .filter_map(|e| e.ok())
66 .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("proto"))
67 .collect();
68
69 proto_files.sort_by(|a, b| a.path().cmp(b.path()));
71
72 for entry in proto_files {
73 let file_hash = Self::hash_file(entry.path())?;
74 hasher.update(&file_hash);
75 }
76
77 Ok(Fingerprint {
78 algorithm: "sha256".to_string(),
79 value: hex::encode(hasher.finalize()),
80 })
81 }
82
83 async fn generate_lock_fingerprint(&self, deps: &[ResolvedDependency]) -> Result<Fingerprint> {
84 let mut hasher = Sha256::new();
85 let mut dep_names: Vec<_> = deps.iter().map(|d| &d.spec.name).collect();
86 dep_names.sort();
87
88 for name in dep_names {
89 hasher.update(name.as_bytes());
90 if let Some(dep) = deps.iter().find(|d| d.spec.name == *name) {
91 hasher.update(dep.fingerprint.as_bytes());
92 }
93 }
94
95 Ok(Fingerprint {
96 algorithm: "sha256".to_string(),
97 value: hex::encode(hasher.finalize()),
98 })
99 }
100}