Skip to main content

multi_member/
multi_member.rs

1//! Multi-member verified transaction (bundle).
2//!
3//! The artifact set is the mutation unit: either every member commits or the
4//! transaction rolls back. Run with
5//! `cargo run -p eggup-core --example multi_member`.
6
7use eggup_core::{
8    AbsentPolicy, AllValidators, ArtifactMember, ArtifactSet, CommitOwnership,
9    ExistingAsOwnedVerifier, InstallPlan, IntegrityRequirement, MemberId, ProductId, ReleaseId,
10    TransactionDisposition,
11};
12use std::fs;
13use std::path::PathBuf;
14use std::time::{SystemTime, UNIX_EPOCH};
15
16fn temp_root(prefix: &str) -> PathBuf {
17    let nanos = SystemTime::now()
18        .duration_since(UNIX_EPOCH)
19        .map(|d| d.as_nanos())
20        .unwrap_or(0);
21    std::env::temp_dir().join(format!("{prefix}-{}-{nanos}", std::process::id()))
22}
23
24fn main() -> Result<(), Box<dyn std::error::Error>> {
25    let base = temp_root("eggup-multi-member");
26    let inputs = base.join("inputs");
27    let root = base.join("install");
28    fs::create_dir_all(&inputs)?;
29    fs::create_dir_all(root.join("bin"))?;
30    fs::write(inputs.join("main"), b"new-main")?;
31    fs::write(inputs.join("helper"), b"new-helper")?;
32    fs::write(root.join("bin/main"), b"old-main")?;
33    fs::write(root.join("bin/helper"), b"old-helper")?;
34    let main_digest = eggup_core::hash_file(&inputs.join("main"))?;
35    let helper_digest = eggup_core::hash_file(&inputs.join("helper"))?;
36    let members = ArtifactSet::new(vec![
37        ArtifactMember::new(MemberId::new("main")?, inputs.join("main"), "bin/main")?
38            .with_integrity(IntegrityRequirement::Sha256(main_digest)),
39        ArtifactMember::new(
40            MemberId::new("helper")?,
41            inputs.join("helper"),
42            "bin/helper",
43        )?
44        .with_integrity(IntegrityRequirement::Sha256(helper_digest)),
45    ])?;
46    let plan = InstallPlan::new(
47        ProductId::new("bundle")?,
48        ReleaseId::new("2.0.0")?,
49        &root,
50        members,
51    )?;
52    let receipt = plan
53        .prepare()?
54        .verify_integrity()?
55        .validate(&AllValidators::new())?
56        .commit(CommitOwnership::new(
57            &ExistingAsOwnedVerifier,
58            AbsentPolicy::AllowCreate,
59        ))?;
60    assert_eq!(receipt.disposition(), TransactionDisposition::Committed);
61    assert_eq!(fs::read(root.join("bin/main"))?, b"new-main");
62    assert_eq!(fs::read(root.join("bin/helper"))?, b"new-helper");
63    let _ = fs::remove_dir_all(&base);
64    println!("multi-member commit: {:?}", receipt.disposition());
65    Ok(())
66}