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> = core::result::Result<T, Error>;
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 f.debug_struct("Error").finish()
206 }
207}
208
209impl From<Error> for c_int {
210 fn from(error: Error) -> Self {
211 error.code
212 }
213}