qubit_fs/temp/persist_failure.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Typed temporary persistence failure.
9
10use std::error::Error;
11use std::fmt::Display;
12use std::fmt::Formatter;
13use std::fmt::Result as FmtResult;
14
15use crate::error::FsError;
16use crate::path::Path;
17use crate::temp::PersistFailureState;
18
19/// Persistence error paired with provider-confirmed partial progress.
20///
21/// # Examples
22///
23/// ```rust
24/// use qubit_fs::error::{FsError, FsErrorKind, FsOperation};
25/// use qubit_fs::temp::{PersistFailure, PersistFailureState};
26///
27/// let failure = PersistFailure::new(
28/// FsError::new(FsErrorKind::Io, FsOperation::PersistTemp, "failed"),
29/// PersistFailureState::NotPublished,
30/// );
31/// assert_eq!(PersistFailureState::NotPublished, failure.state());
32/// ```
33#[derive(Debug)]
34pub struct PersistFailure {
35 /// Contextual filesystem error that interrupted persistence.
36 error: FsError,
37 /// Provider-confirmed source and destination progress.
38 state: PersistFailureState,
39 /// Destination path retained when publication reached a known target.
40 publication_target: Option<Path>,
41}
42
43impl PersistFailure {
44 /// Creates a typed persistence failure.
45 ///
46 /// # Parameters
47 /// - `error`: Underlying filesystem failure.
48 /// - `state`: Confirmed source and target progress.
49 ///
50 /// # Returns
51 /// A failure preserving both the cause and recovery contract.
52 #[inline]
53 #[must_use]
54 pub fn new(error: FsError, state: PersistFailureState) -> Self {
55 Self {
56 error,
57 state,
58 publication_target: None,
59 }
60 }
61
62 /// Retains the destination associated with a known publication attempt.
63 pub(crate) fn with_publication_target(mut self, target: Option<&Path>) -> Self {
64 self.publication_target = target.cloned();
65 self
66 }
67
68 /// Returns provider-confirmed partial progress.
69 ///
70 /// # Returns
71 /// The recovery state for this persist attempt.
72 #[inline]
73 #[must_use]
74 pub const fn state(&self) -> PersistFailureState {
75 self.state
76 }
77
78 /// Returns the underlying filesystem error.
79 ///
80 /// # Returns
81 /// The error with operation, path, provider, and source context.
82 #[inline]
83 #[must_use]
84 pub const fn error(&self) -> &FsError {
85 &self.error
86 }
87
88 /// Returns the target associated with the retained recovery fact.
89 #[inline]
90 #[must_use]
91 pub fn publication_target(&self) -> Option<&Path> {
92 self.publication_target.as_ref()
93 }
94
95 /// Consumes this failure and returns the underlying filesystem error.
96 ///
97 /// # Returns
98 /// The owned filesystem error.
99 #[inline]
100 #[must_use]
101 pub fn into_error(self) -> FsError {
102 self.error
103 }
104
105 /// Splits this facade failure into its causal error and state.
106 #[inline]
107 #[must_use]
108 pub fn into_parts(self) -> (FsError, PersistFailureState) {
109 (self.error, self.state)
110 }
111
112 /// Consumes the failure and returns its complete recovery snapshot.
113 #[inline]
114 #[must_use]
115 pub fn into_recovery_parts(self) -> (FsError, PersistFailureState, Option<Path>) {
116 (self.error, self.state, self.publication_target)
117 }
118}
119
120impl Display for PersistFailure {
121 #[inline]
122 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
123 write!(formatter, "persist {:?}: {}", self.state, self.error)
124 }
125}
126
127impl Error for PersistFailure {
128 #[inline]
129 fn source(&self) -> Option<&(dyn Error + 'static)> {
130 Some(&self.error)
131 }
132}