Skip to main content

qubit_fs/copy/
async_copy_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// facade tests.
9//! Recoverable failure returned by an asynchronous copy operation.
10
11use std::error::Error;
12use std::fmt::Debug;
13use std::fmt::Display;
14use std::fmt::Formatter;
15use std::fmt::Result as FmtResult;
16
17use crate::copy::CopyFailureState;
18use crate::copy::CopyStats;
19use crate::error::FsError;
20
21/// Copy failure facts retained after an asynchronous copy operation.
22///
23/// # Examples
24///
25/// ```rust
26/// use qubit_fs::copy::{AsyncCopyFailure, CopyFailureState, CopyStats};
27/// use qubit_fs::error::{FsError, FsErrorKind, FsOperation};
28///
29/// assert!(std::any::type_name::<AsyncCopyFailure>().contains("AsyncCopyFailure"));
30/// let error = FsError::new(FsErrorKind::Io, FsOperation::Copy, "interrupted");
31/// assert_eq!(CopyFailureState::Unchanged, CopyFailureState::Unchanged);
32/// assert_eq!(0, CopyStats::default().bytes);
33/// assert_eq!(FsOperation::Copy, error.operation());
34/// ```
35pub struct AsyncCopyFailure {
36    /// Contextual filesystem error that caused the copy to fail.
37    error: FsError,
38    /// Confirmed destination publication state at failure time.
39    state: CopyFailureState,
40    /// Transfer progress confirmed before the failure.
41    partial_stats: CopyStats,
42}
43
44impl AsyncCopyFailure {
45    /// Creates a failure from facade-confirmed facts.
46    pub(crate) fn new(error: FsError, state: CopyFailureState, partial_stats: CopyStats) -> Self {
47        Self {
48            error,
49            state,
50            partial_stats,
51        }
52    }
53
54    /// Returns the contextual filesystem error.
55    #[inline]
56    #[must_use]
57    pub const fn error(&self) -> &FsError {
58        &self.error
59    }
60
61    /// Returns the confirmed publication state.
62    #[inline]
63    #[must_use]
64    pub const fn state(&self) -> CopyFailureState {
65        self.state
66    }
67
68    /// Returns partial transfer statistics.
69    #[inline]
70    #[must_use]
71    pub const fn partial_stats(&self) -> &CopyStats {
72        &self.partial_stats
73    }
74
75    /// Splits the failure into owned error, state, and progress facts.
76    #[inline]
77    #[must_use]
78    pub fn into_parts(self) -> (FsError, CopyFailureState, CopyStats) {
79        (self.error, self.state, self.partial_stats)
80    }
81}
82
83impl Debug for AsyncCopyFailure {
84    /// Formats failure facts without exposing a provider session.
85    #[inline]
86    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
87        formatter
88            .debug_struct("AsyncCopyFailure")
89            .field("error", &self.error)
90            .field("state", &self.state)
91            .field("partial_stats", &self.partial_stats)
92            .finish()
93    }
94}
95
96impl Display for AsyncCopyFailure {
97    /// Formats the wrapped file-system error.
98    #[inline]
99    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
100        Display::fmt(self.error(), formatter)
101    }
102}
103
104impl Error for AsyncCopyFailure {
105    /// Returns the underlying file-system error.
106    #[inline]
107    fn source(&self) -> Option<&(dyn Error + 'static)> {
108        Some(self.error())
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::AsyncCopyFailure;
115    use crate::copy::CopyFailureState;
116    use crate::copy::CopyStats;
117    use crate::error::FsError;
118    use crate::error::FsErrorKind;
119    use crate::error::FsOperation;
120
121    #[test]
122    fn owned_parts_are_executed_at_runtime() {
123        let failure = AsyncCopyFailure::new(
124            FsError::new(FsErrorKind::NotFound, FsOperation::Copy, "missing source"),
125            CopyFailureState::Unchanged,
126            CopyStats {
127                files: 1,
128                ..CopyStats::default()
129            },
130        );
131
132        let (error, state, stats) = failure.into_parts();
133        assert_eq!(error.kind(), FsErrorKind::NotFound);
134        assert_eq!(state, CopyFailureState::Unchanged);
135        assert_eq!(stats.files, 1);
136    }
137}