qubit_fs/write/async_write_all_operation_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//! Failure for an owning asynchronous whole-file write.
9use std::error::Error;
10use std::fmt::Display;
11use std::fmt::Formatter;
12
13use crate::error::FsError;
14use crate::write::WriteFailureState;
15/// Error returned by an owning asynchronous whole-file write.
16///
17/// Recovery ownership remains in the operation even when this error is
18/// consumed.
19///
20/// # Examples
21/// ```rust
22/// # mod support { include!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/common/rustdoc_support.rs")); }
23/// # use support::*;
24/// # let (filesystem, _) = async_recording_spi::async_recording_file_system(async_recording_spi::AsyncRecordingConfig {
25/// # writer_commit_failure: Some(qubit_fs::write::WriteFailureState::NotPublished),
26/// # ..Default::default()
27/// # });
28/// # poll_support::ready(async {
29/// use qubit_fs::Path;
30/// use qubit_fs::write::WriteOptions;
31/// use qubit_fs::write::AsyncWriterRecovery;
32/// use qubit_fs::write::WriteFailureState;
33/// let mut operation = filesystem.begin_write_all(
34/// Path::parse("/report")?, b"bytes".to_vec(), WriteOptions::default(),
35/// )?;
36/// let failure = operation.execute().await.expect_err("fixture commit failure");
37/// assert_eq!(WriteFailureState::NotPublished, failure.state());
38/// assert_eq!(5, failure.written_bytes());
39/// assert!(operation.has_recovery());
40/// let cleanup = match operation.recovery().expect("retained session") {
41/// AsyncWriterRecovery::Opened(writer) => writer.abort_async().await,
42/// AsyncWriterRecovery::Rejected(writer) => writer.abort_async().await,
43/// };
44/// assert!(cleanup.is_ok());
45/// // Application error handling can retain `failure`, `cleanup`, and `operation`.
46/// # Ok::<(), Box<dyn std::error::Error>>(())
47/// # }).unwrap();
48/// ```
49#[must_use]
50pub struct AsyncWriteAllOperationFailure {
51 /// Original contextual error.
52 error: FsError,
53 /// Publication facts at failure.
54 state: WriteFailureState,
55 /// Bytes acknowledged by completed writes.
56 written_bytes: u64,
57}
58impl AsyncWriteAllOperationFailure {
59 /// Creates a failure with its recovery facts.
60 pub(crate) fn new(error: FsError, state: WriteFailureState, written_bytes: u64) -> Self {
61 Self {
62 error,
63 state,
64 written_bytes,
65 }
66 }
67 /// Returns the contextual filesystem error.
68 #[must_use]
69 pub const fn error(&self) -> &FsError {
70 &self.error
71 }
72 /// Returns the publication facts recorded when execution stopped.
73 #[must_use]
74 pub const fn state(&self) -> WriteFailureState {
75 self.state
76 }
77 /// Returns bytes acknowledged by completed writes before execution stopped.
78 #[must_use]
79 pub const fn written_bytes(&self) -> u64 {
80 self.written_bytes
81 }
82 /// Consumes the failure and returns its filesystem error.
83 ///
84 /// This discards publication state and confirmed byte count. The operation
85 /// still owns any recovery writer; prefer `into_parts` when recovering.
86 #[must_use]
87 pub fn into_error(self) -> FsError {
88 self.error
89 }
90 /// Consumes the failure and returns all recovery facts.
91 #[must_use]
92 pub fn into_parts(self) -> (FsError, WriteFailureState, u64) {
93 (self.error, self.state, self.written_bytes)
94 }
95}
96impl Display for AsyncWriteAllOperationFailure {
97 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
98 self.error.fmt(f)
99 }
100}
101impl std::fmt::Debug for AsyncWriteAllOperationFailure {
102 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("AsyncWriteAllOperationFailure")
104 .field("error", &self.error)
105 .field("state", &self.state)
106 .field("written_bytes", &self.written_bytes)
107 .finish()
108 }
109}
110impl Error for AsyncWriteAllOperationFailure {
111 fn source(&self) -> Option<&(dyn Error + 'static)> {
112 Some(&self.error)
113 }
114}