custom_validator/
custom_validator.rs1use eggup_core::{
8 AbsentPolicy, ArtifactMember, ArtifactSet, CandidateValidator, CommitOwnership,
9 ExistingAsOwnedVerifier, InstallPlan, IntegrityRequirement, MemberId, ProductId, ReleaseId,
10 TransactionDisposition, VerifiedTransaction,
11};
12use std::fs;
13use std::path::PathBuf;
14use std::time::{SystemTime, UNIX_EPOCH};
15
16#[derive(Debug)]
17struct ContainsMarker {
18 member: MemberId,
19 marker: Vec<u8>,
20}
21
22impl CandidateValidator for ContainsMarker {
23 fn validate(&self, transaction: &VerifiedTransaction) -> eggup_core::Result<()> {
24 let path = transaction.staged_path(&self.member)?;
25 let bytes = fs::read(&path).map_err(|e| {
26 eggup_core::Error::CandidateExecution(format!("reading candidate: {e}"))
27 })?;
28 if bytes.windows(self.marker.len()).any(|w| w == self.marker) {
29 Ok(())
30 } else {
31 Err(eggup_core::Error::CandidateExecution(
32 "marker not found in candidate".into(),
33 ))
34 }
35 }
36}
37
38fn temp_root(prefix: &str) -> PathBuf {
39 let nanos = SystemTime::now()
40 .duration_since(UNIX_EPOCH)
41 .map(|d| d.as_nanos())
42 .unwrap_or(0);
43 std::env::temp_dir().join(format!("{prefix}-{}-{nanos}", std::process::id()))
44}
45
46fn main() -> Result<(), Box<dyn std::error::Error>> {
47 let base = temp_root("eggup-custom-validator");
48 let inputs = base.join("inputs");
49 let root = base.join("install");
50 fs::create_dir_all(&inputs)?;
51 fs::create_dir_all(root.join("bin"))?;
52 fs::write(inputs.join("app"), b"release-marker-42")?;
53 let digest = eggup_core::hash_file(&inputs.join("app"))?;
54 let member = ArtifactMember::new(MemberId::new("main")?, inputs.join("app"), "bin/app")?
55 .with_integrity(IntegrityRequirement::Sha256(digest));
56 let plan = InstallPlan::new(
57 ProductId::new("demo")?,
58 ReleaseId::new("3.0.0")?,
59 &root,
60 ArtifactSet::single(member)?,
61 )?;
62 let validator = ContainsMarker {
63 member: MemberId::new("main")?,
64 marker: b"marker-42".to_vec(),
65 };
66 let receipt = plan
67 .prepare()?
68 .verify_integrity()?
69 .validate(&validator)?
70 .commit(CommitOwnership::new(
71 &ExistingAsOwnedVerifier,
72 AbsentPolicy::AllowCreate,
73 ))?;
74 assert_eq!(receipt.disposition(), TransactionDisposition::Committed);
75 let _ = fs::remove_dir_all(&base);
76 println!("custom validator commit: {:?}", receipt.disposition());
77 Ok(())
78}