Skip to main content

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}