qubit_fs/error/fs_error.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Concrete filesystem error type.
11
12use std::error::Error;
13use std::fmt::{
14 Display,
15 Formatter,
16 Result as FmtResult,
17};
18
19use crate::{
20 FsErrorKind,
21 FsOperation,
22 FsPath,
23};
24
25/// Provider-neutral filesystem error with operation and path context.
26#[derive(Debug)]
27pub struct FsError {
28 /// Error category.
29 pub kind: FsErrorKind,
30 /// Operation that produced the error.
31 pub operation: FsOperation,
32 /// Primary path involved in the operation.
33 pub path: Option<Box<FsPath>>,
34 /// Secondary path involved in the operation.
35 pub target: Option<Box<FsPath>>,
36 /// Provider id or alias involved in the operation.
37 pub provider: Option<Box<str>>,
38 /// Human-readable error message.
39 pub message: Box<str>,
40 /// Lower-level source error.
41 pub source: Option<Box<dyn Error + Send + Sync + 'static>>,
42}
43
44impl FsError {
45 /// Creates a filesystem error without path or provider context.
46 ///
47 /// # Parameters
48 /// - `kind`: Provider-neutral error category.
49 /// - `operation`: Operation that produced the error.
50 /// - `message`: Human-readable diagnostic message.
51 ///
52 /// # Returns
53 /// New filesystem error.
54 #[inline]
55 pub fn new(kind: FsErrorKind, operation: FsOperation, message: &str) -> Self {
56 Self {
57 kind,
58 operation,
59 path: None,
60 target: None,
61 provider: None,
62 message: message.into(),
63 source: None,
64 }
65 }
66
67 /// Creates a filesystem error that wraps a lower-level source error.
68 ///
69 /// # Parameters
70 /// - `kind`: Provider-neutral error category.
71 /// - `operation`: Operation that produced the error.
72 /// - `message`: Human-readable diagnostic message.
73 /// - `source`: Lower-level error to preserve.
74 ///
75 /// # Returns
76 /// New filesystem error with source context.
77 #[inline]
78 pub fn with_source<E>(kind: FsErrorKind, operation: FsOperation, message: &str, source: E) -> Self
79 where
80 E: Error + Send + Sync + 'static,
81 {
82 Self {
83 source: Some(Box::new(source)),
84 ..Self::new(kind, operation, message)
85 }
86 }
87
88 /// Adds primary path context.
89 ///
90 /// # Parameters
91 /// - `path`: Primary path involved in the operation.
92 ///
93 /// # Returns
94 /// Updated filesystem error.
95 #[inline]
96 #[must_use]
97 pub fn with_path(mut self, path: FsPath) -> Self {
98 self.path = Some(Box::new(path));
99 self
100 }
101
102 /// Adds secondary target path context.
103 ///
104 /// # Parameters
105 /// - `target`: Secondary path involved in the operation.
106 ///
107 /// # Returns
108 /// Updated filesystem error.
109 #[inline]
110 #[must_use]
111 pub fn with_target(mut self, target: FsPath) -> Self {
112 self.target = Some(Box::new(target));
113 self
114 }
115
116 /// Adds provider context.
117 ///
118 /// # Parameters
119 /// - `provider`: Provider id or alias involved in the operation.
120 ///
121 /// # Returns
122 /// Updated filesystem error.
123 #[inline]
124 #[must_use]
125 pub fn with_provider(mut self, provider: &str) -> Self {
126 self.provider = Some(provider.into());
127 self
128 }
129
130 /// Creates an invalid-path error.
131 ///
132 /// # Parameters
133 /// - `operation`: Operation that rejected the path.
134 /// - `message`: Human-readable reason.
135 ///
136 /// # Returns
137 /// Invalid-path filesystem error.
138 #[inline]
139 pub fn invalid_path(operation: FsOperation, message: &str) -> Self {
140 Self::new(FsErrorKind::InvalidPath, operation, message)
141 }
142
143 /// Gets the error kind.
144 ///
145 /// # Returns
146 /// Error category.
147 #[inline]
148 #[must_use]
149 pub fn kind(&self) -> FsErrorKind {
150 self.kind
151 }
152}
153
154impl Display for FsError {
155 #[inline]
156 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
157 write!(
158 formatter,
159 "{:?} failed with {:?}: {}",
160 self.operation, self.kind, self.message,
161 )
162 }
163}
164
165impl Error for FsError {
166 #[inline]
167 fn source(&self) -> Option<&(dyn Error + 'static)> {
168 self.source.as_deref().map(|source| source as &(dyn Error + 'static))
169 }
170}