file_system/
error.rs

1//! Error types and result handling for file system operations.
2//!
3//! This module defines all possible errors that can occur during file system operations,
4//! along with conversion traits and display implementations for comprehensive error reporting.
5
6use core::{fmt::Display, num::NonZeroU32};
7
8/// Standard result type for file system operations.
9///
10/// This is a convenience alias for `Result<T, Error>` used throughout the file system crate.
11/// All file system operations that can fail return this type.
12///
13/// # Examples
14///
15/// ```rust
16/// # extern crate alloc;
17/// use file_system::{Result, Error};
18///
19/// fn example_operation() -> Result<String> {
20///     Ok("Success".into())
21/// }
22///
23/// fn failing_operation() -> Result<()> {
24///     Err(Error::PermissionDenied)
25/// }
26/// ```
27pub type Result<T> = core::result::Result<T, Error>;
28
29/// Comprehensive enumeration of all possible file system errors.
30///
31/// This enum covers errors that can occur at various levels of the file system stack,
32/// from low-level device operations to high-level file system operations. Each variant
33/// has a unique numeric discriminant for FFI compatibility.
34#[derive(Debug, PartialEq, Clone, Copy, Eq)]
35#[repr(C)]
36pub enum Error {
37    /// Failed to initialize the file system.
38    FailedToInitializeFileSystem = 1,
39    /// Permission denied for the requested operation.
40    PermissionDenied,
41    /// File, directory, or resource not found.
42    NotFound,
43    /// File or directory already exists.
44    AlreadyExists,
45    /// Directory already exists (more specific than Already_exists).
46    DirectoryAlreadyExists,
47    /// File system is full and cannot store more data.
48    FileSystemFull,
49    /// Generic file system error.
50    FileSystemError,
51    /// The provided path is invalid or malformed.
52    InvalidPath,
53    /// The file is corrupted or invalid.
54    InvalidFile,
55    /// The directory is corrupted or invalid.
56    InvalidDirectory,
57    /// The symbolic link is invalid or broken.
58    InvalidSymbolicLink,
59    /// Unknown or unspecified error.
60    Unknown,
61    /// File or task identifier is invalid.
62    InvalidIdentifier,
63    /// Failed to retrieve task information from task manager.
64    FailedToGetTaskInformations,
65    /// Failed to retrieve user information from user manager.
66    FailedToGetUsersInformations,
67    /// Maximum number of mounted file systems exceeded.
68    TooManyMountedFileSystems,
69    /// Maximum number of open files exceeded.
70    TooManyOpenFiles,
71    /// Internal implementation error.
72    InternalError,
73    /// Invalid access mode specified.
74    InvalidMode,
75    /// Operation is not supported by the device or file system.
76    UnsupportedOperation,
77    /// Resource is temporarily busy or unavailable.
78    RessourceBusy,
79    /// System or component is already initialized.
80    AlreadyInitialized,
81    /// System or component is not initialized.
82    NotInitialized,
83    /// Failed to get users manager instance.
84    FailedToGetUsersManagerInstance,
85    /// Failed to get task manager instance.
86    FailedToGetTaskManagerInstance,
87    /// Invalid parameter provided to function.
88    InvalidParameter,
89    /// Invalid flags specified for operation.
90    InvalidFlags,
91    /// Expected a directory but found a file.
92    NotDirectory,
93    /// Expected a file but found a directory.
94    IsDirectory,
95    /// Input/output error during operation.
96    InputOutput,
97    /// Directory is not empty and cannot be removed.
98    DirectoryNotEmpty,
99    /// File size exceeds maximum allowed size.
100    FileTooLarge,
101    /// Requested attribute does not exist.
102    NoAttribute,
103    /// File or directory name is too long.
104    NameTooLong,
105    /// Data corruption detected.
106    Corrupted,
107    /// Insufficient memory for operation.
108    NoMemory,
109    /// No space left on device.
110    NoSpaceLeft,
111    /// Error in timestamp or time-related operation.
112    TimeError,
113    /// Invalid inode reference.
114    InvalidInode,
115    /// Not mounted.
116    NotMounted,
117    /// Already mounted.
118    AlreadyMounted,
119    /// Invalid context
120    InvalidContext,
121    /// Other unclassified error.
122    Other,
123}
124
125impl core::error::Error for Error {}
126
127impl Error {
128    /// Get the numeric discriminant of the error as a non-zero u32.
129    ///
130    /// This is useful for FFI operations where errors need to be represented
131    /// as numeric codes.
132    ///
133    /// # Returns
134    ///
135    /// A `NonZeroU32` containing the error's discriminant value.
136    ///
137    /// # Examples
138    ///
139    /// ```rust
140    /// # extern crate alloc;
141    /// use file_system::Error;
142    ///
143    /// let error = Error::PermissionDenied;
144    /// let code = error.get_discriminant();
145    /// assert_eq!(code.get(), 2); // Permission_denied has discriminant 2
146    /// ```
147    pub fn get_discriminant(&self) -> NonZeroU32 {
148        unsafe { NonZeroU32::new_unchecked(*self as u32) }
149    }
150}
151
152/// Convert Task module errors to file system errors.
153///
154/// This allows transparent handling of task-related errors in file system operations.
155impl From<task::Error> for Error {
156    fn from(_: task::Error) -> Self {
157        Error::FailedToGetTaskInformations
158    }
159}
160
161/// Convert Users module errors to file system errors.
162///
163/// This allows transparent handling of user-related errors in file system operations.
164impl From<users::Error> for Error {
165    fn from(_: users::Error) -> Self {
166        Error::FailedToGetUsersInformations
167    }
168}
169
170/// Convert file system errors to numeric discriminants.
171///
172/// This conversion is useful for FFI where errors need to be represented as numbers.
173impl From<Error> for NonZeroU32 {
174    fn from(error: Error) -> Self {
175        error.get_discriminant()
176    }
177}
178
179/// Display implementation for user-friendly error messages.
180///
181/// Provides human-readable descriptions of all error variants.
182impl Display for Error {
183    fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
184        let string = match self {
185            Error::FailedToInitializeFileSystem => "Failed to initialize file system",
186            Error::PermissionDenied => "Permission denied",
187            Error::NotFound => "Not found",
188            Error::AlreadyExists => "Already exists",
189            Error::DirectoryAlreadyExists => "Directory already exists",
190            Error::FileSystemFull => "File system full",
191            Error::FileSystemError => "File system error",
192            Error::InvalidPath => "Invalid path",
193            Error::InvalidFile => "Invalid file",
194            Error::InvalidDirectory => "Invalid directory",
195            Error::InvalidSymbolicLink => "Invalid symbolic link",
196            Error::Unknown => "Unknown",
197            Error::InvalidIdentifier => "Invalid identifier",
198            Error::FailedToGetTaskInformations => "Failed to get task informations",
199            Error::FailedToGetUsersInformations => "Failed to get users informations",
200            Error::TooManyMountedFileSystems => "Too many mounted file systems",
201            Error::TooManyOpenFiles => "Too many open files",
202            Error::InternalError => "Internal error",
203            Error::InvalidMode => "Invalid mode",
204            Error::UnsupportedOperation => "Unsupported operation",
205            Error::RessourceBusy => "Ressource busy",
206            Error::AlreadyInitialized => "Already initialized",
207            Error::NotInitialized => "Not initialized",
208            Error::FailedToGetUsersManagerInstance => "Failed to get users manager instance",
209            Error::FailedToGetTaskManagerInstance => "Failed to get task manager instance",
210            Error::InvalidParameter => "Invalid parameter",
211            Error::InvalidFlags => "Invalid flags",
212            Error::NotDirectory => "Not directory",
213            Error::IsDirectory => "Is directory",
214            Error::InputOutput => "Input output",
215            Error::DirectoryNotEmpty => "Directory not empty",
216            Error::FileTooLarge => "File too large",
217            Error::NoAttribute => "No attribute",
218            Error::NameTooLong => "Name too long",
219            Error::Corrupted => "Corrupted",
220            Error::NoMemory => "No memory",
221            Error::NoSpaceLeft => "No space left",
222            Error::TimeError => "Time error",
223            Error::InvalidInode => "Invalid inode",
224            Error::NotMounted => "Not mounted",
225            Error::AlreadyMounted => "Already mounted",
226            Error::InvalidContext => "Invalid context",
227            Error::Other => "Other",
228        };
229
230        write!(formatter, "{string}")
231    }
232}
233
234impl embedded_io_async::Error for Error {
235    fn kind(&self) -> embedded_io_async::ErrorKind {
236        match self {
237            Error::PermissionDenied => embedded_io_async::ErrorKind::PermissionDenied,
238            Error::NotFound => embedded_io_async::ErrorKind::NotFound,
239            Error::AlreadyExists | Error::DirectoryAlreadyExists => {
240                embedded_io_async::ErrorKind::AlreadyExists
241            }
242            Error::FileSystemFull | Error::NoSpaceLeft => embedded_io_async::ErrorKind::OutOfMemory,
243            Error::InputOutput => embedded_io_async::ErrorKind::Interrupted,
244            _ => embedded_io_async::ErrorKind::Other,
245        }
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252    use alloc::format;
253
254    #[test]
255    fn test_error_discriminants() {
256        // Test that each error has a unique discriminant
257        assert_eq!(
258            Error::FailedToInitializeFileSystem.get_discriminant().get(),
259            1
260        );
261        assert_eq!(Error::PermissionDenied.get_discriminant().get(), 2);
262        assert_eq!(Error::NotFound.get_discriminant().get(), 3);
263        assert_eq!(Error::AlreadyExists.get_discriminant().get(), 4);
264        assert_eq!(Error::DirectoryAlreadyExists.get_discriminant().get(), 5);
265
266        // Test a few more to ensure discriminants are sequential
267        assert_eq!(Error::FileSystemFull.get_discriminant().get(), 6);
268        assert_eq!(Error::FileSystemError.get_discriminant().get(), 7);
269        assert_eq!(Error::InvalidPath.get_discriminant().get(), 8);
270    }
271
272    #[test]
273    fn test_error_display() {
274        // Test display formatting for all error types
275        assert_eq!(
276            format!("{}", Error::FailedToInitializeFileSystem),
277            "Failed to initialize file system"
278        );
279        assert_eq!(format!("{}", Error::PermissionDenied), "Permission denied");
280        assert_eq!(format!("{}", Error::NotFound), "Not found");
281        assert_eq!(format!("{}", Error::AlreadyExists), "Already exists");
282        assert_eq!(
283            format!("{}", Error::DirectoryAlreadyExists),
284            "Directory already exists"
285        );
286        assert_eq!(format!("{}", Error::FileSystemFull), "File system full");
287        assert_eq!(format!("{}", Error::FileSystemError), "File system error");
288        assert_eq!(format!("{}", Error::InvalidPath), "Invalid path");
289        assert_eq!(format!("{}", Error::InvalidFile), "Invalid file");
290        assert_eq!(format!("{}", Error::InvalidDirectory), "Invalid directory");
291        assert_eq!(
292            format!("{}", Error::InvalidSymbolicLink),
293            "Invalid symbolic link"
294        );
295        assert_eq!(format!("{}", Error::Unknown), "Unknown");
296        assert_eq!(
297            format!("{}", Error::InvalidIdentifier),
298            "Invalid identifier"
299        );
300        assert_eq!(
301            format!("{}", Error::FailedToGetTaskInformations),
302            "Failed to get task informations"
303        );
304        assert_eq!(
305            format!("{}", Error::FailedToGetUsersInformations),
306            "Failed to get users informations"
307        );
308        assert_eq!(
309            format!("{}", Error::TooManyMountedFileSystems),
310            "Too many mounted file systems"
311        );
312        assert_eq!(
313            format!("{}", Error::TooManyOpenFiles),
314            "Too many open files"
315        );
316        assert_eq!(format!("{}", Error::InternalError), "Internal error");
317        assert_eq!(format!("{}", Error::InvalidMode), "Invalid mode");
318        assert_eq!(
319            format!("{}", Error::UnsupportedOperation),
320            "Unsupported operation"
321        );
322        assert_eq!(format!("{}", Error::RessourceBusy), "Ressource busy");
323        assert_eq!(
324            format!("{}", Error::AlreadyInitialized),
325            "Already initialized"
326        );
327        assert_eq!(format!("{}", Error::NotInitialized), "Not initialized");
328        assert_eq!(
329            format!("{}", Error::FailedToGetUsersManagerInstance),
330            "Failed to get users manager instance"
331        );
332        assert_eq!(
333            format!("{}", Error::FailedToGetTaskManagerInstance),
334            "Failed to get task manager instance"
335        );
336        assert_eq!(format!("{}", Error::InvalidParameter), "Invalid parameter");
337        assert_eq!(format!("{}", Error::InvalidFlags), "Invalid flags");
338        assert_eq!(format!("{}", Error::NotDirectory), "Not directory");
339        assert_eq!(format!("{}", Error::IsDirectory), "Is directory");
340        assert_eq!(format!("{}", Error::InputOutput), "Input output");
341        assert_eq!(
342            format!("{}", Error::DirectoryNotEmpty),
343            "Directory not empty"
344        );
345        assert_eq!(format!("{}", Error::FileTooLarge), "File too large");
346        assert_eq!(format!("{}", Error::NoAttribute), "No attribute");
347        assert_eq!(format!("{}", Error::NameTooLong), "Name too long");
348        assert_eq!(format!("{}", Error::Corrupted), "Corrupted");
349        assert_eq!(format!("{}", Error::NoMemory), "No memory");
350        assert_eq!(format!("{}", Error::NoSpaceLeft), "No space left");
351        assert_eq!(format!("{}", Error::TimeError), "Time error");
352        assert_eq!(format!("{}", Error::InvalidInode), "Invalid inode");
353        assert_eq!(format!("{}", Error::Other), "Other");
354    }
355
356    #[test]
357    fn test_error_debug() {
358        // Test debug formatting
359        let error = Error::PermissionDenied;
360        let debug_str = format!("{error:?}");
361        assert_eq!(debug_str, "PermissionDenied");
362    }
363
364    #[test]
365    fn test_error_equality() {
366        // Test equality and cloning
367        let error1 = Error::NotFound;
368        let error2 = Error::NotFound;
369        let error3 = Error::PermissionDenied;
370
371        assert_eq!(error1, error2);
372        assert_ne!(error1, error3);
373
374        let cloned = error1;
375        assert_eq!(error1, cloned);
376    }
377
378    #[test]
379    fn test_error_conversions() {
380        // Test conversion to NonZeroU32
381        let error = Error::NotFound;
382        let discriminant: NonZeroU32 = error.into();
383        assert_eq!(discriminant.get(), 3);
384
385        // Test explicit discriminant access
386        assert_eq!(error.get_discriminant().get(), 3);
387    }
388
389    #[test]
390    fn test_result_type() {
391        // Test the Result alias
392        let success: Result<i32> = Ok(42);
393        let failure: Result<i32> = Err(Error::PermissionDenied);
394
395        assert_eq!(success, Ok(42));
396
397        assert_eq!(failure, Err(Error::PermissionDenied));
398    }
399
400    #[test]
401    fn test_error_categories() {
402        // Test that errors can be categorized by their discriminant ranges
403
404        // Initialization errors (1-3 range roughly)
405        assert!(matches!(
406            Error::FailedToInitializeFileSystem.get_discriminant().get(),
407            1
408        ));
409        assert!(matches!(
410            Error::AlreadyInitialized.get_discriminant().get(),
411            22
412        ));
413        assert!(matches!(Error::NotInitialized.get_discriminant().get(), 23));
414
415        // Permission errors
416        assert!(matches!(
417            Error::PermissionDenied.get_discriminant().get(),
418            2
419        ));
420        assert!(matches!(Error::InvalidMode.get_discriminant().get(), 19));
421
422        // File/Directory errors
423        assert!(matches!(Error::NotFound.get_discriminant().get(), 3));
424        assert!(matches!(Error::AlreadyExists.get_discriminant().get(), 4));
425        assert!(matches!(
426            Error::DirectoryAlreadyExists.get_discriminant().get(),
427            5
428        ));
429    }
430
431    #[test]
432    fn test_error_copy_semantics() {
433        // Test that Error implements Copy
434        let error = Error::FileSystemFull;
435        let copied = error; // This should work due to Copy trait
436
437        // Both should be usable
438        assert_eq!(error, Error::FileSystemFull);
439        assert_eq!(copied, Error::FileSystemFull);
440        assert_eq!(error, copied);
441    }
442
443    #[test]
444    fn test_error_size() {
445        // Ensure Error has a reasonable size for an enum
446        use core::mem::size_of;
447
448        // Should be small since it's a C-style enum
449        assert!(size_of::<Error>() <= 4); // Should be 4 bytes or less
450    }
451
452    #[test]
453    fn test_nonzero_conversion() {
454        // Test that all errors convert to valid NonZeroU32
455        let errors = [
456            Error::FailedToInitializeFileSystem,
457            Error::PermissionDenied,
458            Error::NotFound,
459            Error::AlreadyExists,
460            Error::DirectoryAlreadyExists,
461            Error::FileSystemFull,
462            Error::FileSystemError,
463            Error::InvalidPath,
464            Error::InvalidFile,
465            Error::InvalidDirectory,
466            Error::InvalidSymbolicLink,
467            Error::Unknown,
468            Error::InvalidIdentifier,
469            Error::FailedToGetTaskInformations,
470            Error::FailedToGetUsersInformations,
471            Error::TooManyMountedFileSystems,
472            Error::TooManyOpenFiles,
473            Error::InternalError,
474            Error::InvalidMode,
475            Error::UnsupportedOperation,
476            Error::RessourceBusy,
477            Error::AlreadyInitialized,
478            Error::NotInitialized,
479            Error::FailedToGetUsersManagerInstance,
480            Error::FailedToGetTaskManagerInstance,
481            Error::InvalidParameter,
482            Error::InvalidFlags,
483            Error::NotDirectory,
484            Error::IsDirectory,
485            Error::InputOutput,
486            Error::DirectoryNotEmpty,
487            Error::FileTooLarge,
488            Error::NoAttribute,
489            Error::NameTooLong,
490            Error::Corrupted,
491            Error::NoMemory,
492            Error::NoSpaceLeft,
493            Error::TimeError,
494            Error::InvalidInode,
495            Error::Other,
496        ];
497
498        for error in errors.iter() {
499            let discriminant = error.get_discriminant();
500            assert!(discriminant.get() > 0);
501
502            let converted: NonZeroU32 = (*error).into();
503            assert_eq!(discriminant, converted);
504        }
505    }
506}