#[non_exhaustive]pub enum FailureCategory {
LockContention,
OwnershipConflict,
Verification,
Filesystem,
InvalidInput,
Injected,
PostCommitCheck,
}Expand description
A stable machine-readable classification of a produced transaction failure.
Every member of this enum describes a failure that can actually occur. Five
of them are produced by the crate’s internal error-to-category mapping in a
shipped build, and a sixth — Injected — is produced by
that same mapping only in this crate’s own test builds.
PostCommitCheck is produced directly when a
caller-supplied post-commit check fails or panics.
Injected is produced only by this crate’s own test
fault harness and never by a shipped build.
The enum is #[non_exhaustive], so a downstream matcher must already
carry a wildcard arm; a category added later therefore reaches consumers
without breaking their compilation. The as_str string is
the only stable identifier a consumer may persist or aggregate, because a
downstream crate cannot enumerate the categories itself.
Retained evidence is deliberately not a category. It is reported as a
TransactionDisposition::RecoveryRequired disposition together with
CleanupDisposition::RetainedForRecovery and a real
TransactionReceipt::recovery_path, never as a failure category.
See architecture/core-transaction.md §8 “Failure semantics” and
crates/eggup-core/docs/transaction.md for the authoritative rules.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
LockContention
Another transaction or ambiguous lock owns the domain.
Produced from Error::UpdateInProgress.
OwnershipConflict
A destination is foreign, unknown, or violates containment/parent rules.
Produced from Error::DestinationConflict.
Verification
Staged bytes changed, are unreadable, or lack required integrity evidence.
Produced from Error::VerificationFailed and
Error::CandidateExecution.
Filesystem
A live filesystem mutation failed.
Produced from Error::Io.
InvalidInput
Caller input or transaction configuration is invalid.
Produced from Error::InvalidInput and Error::UnknownMember.
Injected
The failure was injected by the test fault harness.
Produced only by this crate’s own test builds, from the
#[cfg(test)]-only Error::Injected. A shipped eggup-core build
never produces this category.
PostCommitCheck
A caller-supplied post-commit check failed or panicked.
Produced directly by the commit path, not through the error-to-category mapping.
Implementations§
Source§impl FailureCategory
impl FailureCategory
Sourcepub fn as_str(&self) -> &'static str
pub fn as_str(&self) -> &'static str
Returns the stable category name.
The returned string is the only stable identifier a consumer may
persist or aggregate: a downstream crate cannot enumerate the members
of a #[non_exhaustive] enum. The strings are a published contract
and do not change.
Examples found in repository?
61fn main() -> Result<(), Box<dyn std::error::Error>> {
62 // 1. Committed: success.
63 let (base, plan) = build(b"new", Some(b"old"))?;
64 let receipt = plan
65 .prepare()?
66 .verify_integrity()?
67 .validate(&AllValidators::new())?
68 .commit(CommitOwnership::new(
69 &ExistingAsOwnedVerifier,
70 AbsentPolicy::AllowCreate,
71 ))?;
72 assert_eq!(receipt.disposition(), TransactionDisposition::Committed);
73 assert!(receipt.failure().is_none());
74 println!("committed: {:?}", receipt.disposition());
75 let _ = fs::remove_dir_all(&base);
76
77 // 2. RolledBack: ownership denied, live state unchanged, failure explains why.
78 let (base, plan) = build(b"new", Some(b"old"))?;
79 let root = base.join("install");
80 let receipt = plan
81 .prepare()?
82 .verify_integrity()?
83 .validate(&AllValidators::new())?
84 .commit(CommitOwnership::new(&DenyAll, AbsentPolicy::AllowCreate))?;
85 assert_eq!(receipt.disposition(), TransactionDisposition::RolledBack);
86 assert!(receipt.rollback_verified());
87 let failure = receipt.failure().expect("rolled back carries a cause");
88 println!(
89 "rolled back: phase={} category={} detail={}",
90 failure.phase().as_str(),
91 failure.category().as_str(),
92 failure.detail()
93 );
94 assert_eq!(fs::read(root.join("bin/app"))?, b"old");
95 assert_ne!(
96 receipt.disposition(),
97 TransactionDisposition::Committed,
98 "rollback is not success"
99 );
100 let _ = fs::remove_dir_all(&base);
101
102 // 3. RecoveryRequired is distinct and high severity: it means Eggup cannot
103 // prove either generation is coherent. It is produced by rollback failure
104 // or unclean finalization; operators must inspect `recovery_path` and the
105 // preserved lock. See `docs/transaction.md` for the full contract.
106 println!("recovery-required: inspect recovery_path + lock; do not retry blindly");
107
108 // 4. A failed post-commit check can keep the coherent new generation,
109 // while remaining machine-readable as a failed check.
110 let (base, plan) = build(b"new", Some(b"old"))?;
111 let receipt = plan
112 .prepare()?
113 .verify_integrity()?
114 .validate(&AllValidators::new())?
115 .commit_with_post_commit(
116 CommitOwnership::new(&ExistingAsOwnedVerifier, AbsentPolicy::AllowCreate),
117 PostCommitFailurePolicy::KeepInstalled,
118 || Err("example health check failed"),
119 )?;
120 assert_eq!(receipt.disposition(), TransactionDisposition::Committed);
121 assert!(receipt.post_commit_failure().is_some());
122 println!(
123 "kept installed after failed check: {:?}",
124 receipt.disposition()
125 );
126 let _ = fs::remove_dir_all(base);
127 Ok(())
128}