littlefs2_core/
io.rs

1//! Traits and types for core I/O functionality.
2
3use core::{
4    ffi::c_int,
5    fmt::{self, Debug, Formatter},
6};
7
8/// The `Read` trait allows for reading bytes from a file.
9pub trait Read {
10    /// Read at most buf.len() bytes.
11    /// Upon success, return how many bytes were read.
12    fn read(&self, buf: &mut [u8]) -> Result<usize>;
13
14    fn read_exact(&self, buf: &mut [u8]) -> Result<()> {
15        // Same assumption as for `read_to_end`.
16        let len = self.read(buf)?;
17        if len == buf.len() {
18            Ok(())
19        } else {
20            // TODO: Decide whether to add an equivalent of `ErrorKind::UnexpectedEof`
21            Err(Error::IO)
22        }
23    }
24}
25
26/** The `Write` trait allows for writing bytes to a file.
27
28By analogy with `std::io::Write`, we also define a `flush()`
29method. In the current implementation, writes are final and
30flush has no effect.
31*/
32pub trait Write {
33    /// Write at most data.len() bytes.
34    /// The file will not necessarily be updated unless
35    /// flush is called as there is a cache.
36    /// Upon success, return how many bytes were written.
37    fn write(&self, data: &[u8]) -> Result<usize>;
38
39    /// Write out all pending writes to storage.
40    fn flush(&self) -> Result<()>;
41
42    fn write_all(&self, mut buf: &[u8]) -> Result<()> {
43        while !buf.is_empty() {
44            match self.write(buf) {
45                Ok(0) => {
46                    // failed to write whole buffer
47                    return Err(Error::IO);
48                }
49                Ok(n) => buf = &buf[n..],
50                Err(e) => return Err(e),
51            }
52        }
53        Ok(())
54    }
55}
56
57/** Enumeration of possible methods to seek within an I/O object.
58
59Use the [`Seek`](../io/trait.Seek.html) trait.
60*/
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62pub enum SeekFrom {
63    Start(u32),
64    End(i32),
65    Current(i32),
66}
67
68impl SeekFrom {
69    pub fn off(self) -> i32 {
70        match self {
71            SeekFrom::Start(u) => u as i32,
72            SeekFrom::End(i) => i,
73            SeekFrom::Current(i) => i,
74        }
75    }
76
77    pub fn whence(self) -> c_int {
78        match self {
79            SeekFrom::Start(_) => 0,
80            SeekFrom::End(_) => 2,
81            SeekFrom::Current(_) => 1,
82        }
83    }
84}
85
86/// Enumeration of possible methods to seek within an file that was just opened
87/// Used in the `read_chunk` and `write_chunk` methods,
88/// Where [`SeekFrom::Current`] would not make sense.
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub enum OpenSeekFrom {
91    Start(u32),
92    End(i32),
93}
94
95impl From<OpenSeekFrom> for SeekFrom {
96    fn from(value: OpenSeekFrom) -> Self {
97        match value {
98            OpenSeekFrom::Start(o) => Self::Start(o),
99            OpenSeekFrom::End(o) => Self::End(o),
100        }
101    }
102}
103
104/** The `Seek` trait provides a cursor which can be moved within a file.
105
106It is possible to seek relative to either end or the current offset.
107*/
108pub trait Seek {
109    /// Seek to an offset in bytes.
110    /// If successful, returns the new position from start of file.
111    fn seek(&self, pos: SeekFrom) -> Result<usize>;
112}
113
114pub type Result<T, E = Error> = core::result::Result<T, E>;
115
116/// The error type for filesystem operations.
117///
118/// Specific error codes are available as associated constants of this type.
119///
120/// ```
121/// # use littlefs2_core::Error;
122/// assert_eq!(Error::IO.code(), -5);
123/// assert_eq!(Error::new(-5), Some(Error::IO));
124/// ```
125#[derive(Clone, Copy, PartialEq)]
126pub struct Error {
127    code: c_int,
128}
129
130impl Error {
131    /// Input / output error occurred.
132    pub const IO: Self = Self::new_const(-5);
133
134    /// File or filesystem was corrupt.
135    pub const CORRUPTION: Self = Self::new_const(-84);
136
137    /// No entry found with that name.
138    pub const NO_SUCH_ENTRY: Self = Self::new_const(-2);
139
140    /// File or directory already exists.
141    pub const ENTRY_ALREADY_EXISTED: Self = Self::new_const(-17);
142
143    /// Path name is not a directory.
144    pub const PATH_NOT_DIR: Self = Self::new_const(-20);
145
146    /// Path specification is to a directory.
147    pub const PATH_IS_DIR: Self = Self::new_const(-21);
148
149    /// Directory was not empty.
150    pub const DIR_NOT_EMPTY: Self = Self::new_const(-39);
151
152    /// Bad file descriptor.
153    pub const BAD_FILE_DESCRIPTOR: Self = Self::new_const(-9);
154
155    /// File is too big.
156    pub const FILE_TOO_BIG: Self = Self::new_const(-27);
157
158    /// Incorrect value specified to function.
159    pub const INVALID: Self = Self::new_const(-22);
160
161    /// No space left available for operation.
162    pub const NO_SPACE: Self = Self::new_const(-28);
163
164    /// No memory available for completing request.
165    pub const NO_MEMORY: Self = Self::new_const(-12);
166
167    /// No attribute or data available
168    pub const NO_ATTRIBUTE: Self = Self::new_const(-61);
169
170    /// Filename too long
171    pub const FILENAME_TOO_LONG: Self = Self::new_const(-36);
172
173    /// Construct an `Error` from an error code.
174    ///
175    /// Return values that are greater or equals to zero represent success.  In this case, `None`
176    /// is returned.
177    pub const fn new(code: c_int) -> Option<Self> {
178        if code >= 0 {
179            None
180        } else {
181            Some(Self { code })
182        }
183    }
184
185    const fn new_const(code: c_int) -> Self {
186        if code >= 0 {
187            panic!("error code must be negative");
188        }
189        Self { code }
190    }
191
192    /// Return the error code of this error.
193    pub const fn code(&self) -> c_int {
194        self.code
195    }
196}
197
198/// Prints a static string as the debug representation.
199///
200/// If unwrap or expect is used on a `Result<_, Error>`, the `Debug` implementation is not
201/// always optimized out.  This leads to a significant increase of the binary size.
202/// As a short-term fix, the `Debug` implementation currently always returns a static string.
203impl Debug for Error {
204    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
205        #[cfg(not(feature = "debug-error"))]
206        {
207            f.debug_struct("Error").finish()
208        }
209        #[cfg(feature = "debug-error")]
210        {
211            match self {
212                &Self::IO => f.write_str("IO"),
213                &Self::CORRUPTION => f.write_str("CORRUPTION"),
214                &Self::NO_SUCH_ENTRY => f.write_str("NO_SUCH_ENTRY"),
215                &Self::ENTRY_ALREADY_EXISTED => f.write_str("ENTRY_ALREADY_EXISTED"),
216                &Self::PATH_NOT_DIR => f.write_str("PATH_NOT_DIR"),
217                &Self::PATH_IS_DIR => f.write_str("PATH_IS_DIR"),
218                &Self::DIR_NOT_EMPTY => f.write_str("DIR_NOT_EMPTY"),
219                &Self::BAD_FILE_DESCRIPTOR => f.write_str("BAD_FILE_DESCRIPTOR"),
220                &Self::FILE_TOO_BIG => f.write_str("FILE_TOO_BIG"),
221                &Self::INVALID => f.write_str("INVALID"),
222                &Self::NO_SPACE => f.write_str("NO_SPACE"),
223                &Self::NO_MEMORY => f.write_str("NO_MEMORY"),
224                &Self::NO_ATTRIBUTE => f.write_str("NO_ATTRIBUTE"),
225                &Self::FILENAME_TOO_LONG => f.write_str("FILENAME_TOO_LONG"),
226                other => f.debug_tuple("Error").field(&other.code).finish(),
227            }
228        }
229    }
230}
231
232impl From<Error> for c_int {
233    fn from(error: Error) -> Self {
234        error.code
235    }
236}