Skip to main content

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