simple_fatfs/fat/direntry/
public.rs1use super::*;
2
3use core::ops;
4
5use crate::*;
6
7#[cfg(not(feature = "std"))]
8use alloc::{borrow::ToOwned, boxed::Box, string::String};
9
10use ::time;
11use bincode::{Decode, Encode};
12use embedded_io::*;
13use time::{Date, PrimitiveDateTime};
14
15#[derive(Debug, Clone, Copy)]
17pub struct Attributes {
18 pub read_only: bool,
20 pub hidden: bool,
23 pub system: bool,
26 pub archive: bool,
31}
32
33impl From<RawAttributes> for Attributes {
34 fn from(value: RawAttributes) -> Self {
35 Attributes {
36 read_only: value.contains(RawAttributes::READ_ONLY),
37 hidden: value.contains(RawAttributes::HIDDEN),
38 system: value.contains(RawAttributes::SYSTEM),
39 archive: value.contains(RawAttributes::ARCHIVE),
40 }
41 }
42}
43
44pub(crate) const DIRENTRY_SIZE: usize = 32;
46
47pub(crate) const SFN_NAME_LEN: usize = 8;
48pub(crate) const SFN_EXT_LEN: usize = 3;
49pub(crate) const SFN_LEN: usize = SFN_NAME_LEN + 1 + SFN_EXT_LEN;
51
52#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, Eq)]
53pub(crate) struct Sfn {
59 pub(crate) name: [u8; SFN_NAME_LEN],
60 pub(crate) ext: [u8; SFN_EXT_LEN],
61}
62
63pub(crate) const CURRENT_DIR_SFN: Sfn = Sfn {
64 name: {
65 use typed_path::constants::windows::CURRENT_DIR;
66
67 let mut s = [b' '; SFN_NAME_LEN];
69 s[0] = CURRENT_DIR[0];
71 s
72 },
73 ext: [b' '; SFN_EXT_LEN],
74};
75
76pub(crate) const PARENT_DIR_SFN: Sfn = Sfn {
77 name: {
78 use typed_path::constants::windows::PARENT_DIR;
79
80 let mut s = [b' '; SFN_NAME_LEN];
82 s[0] = PARENT_DIR[0];
84 s[1] = PARENT_DIR[1];
85 s
86 },
87 ext: [b' '; SFN_EXT_LEN],
88};
89
90impl Sfn {
91 fn get_byte_slice(&self) -> [u8; SFN_NAME_LEN + SFN_EXT_LEN] {
92 let mut slice = [0; SFN_NAME_LEN + SFN_EXT_LEN];
93
94 slice[..SFN_NAME_LEN].copy_from_slice(&self.name);
95 slice[SFN_NAME_LEN..].copy_from_slice(&self.ext);
96
97 slice
98 }
99
100 pub(crate) fn gen_checksum(&self) -> u8 {
101 let mut sum = 0;
102
103 for c in self.get_byte_slice() {
104 sum = (if (sum & 1) != 0 { 0x80_u8 } else { 0_u8 })
105 .wrapping_add(sum >> 1)
106 .wrapping_add(c)
107 }
108
109 sum
110 }
111
112 pub(crate) fn decode(&self, codepage: &Codepage) -> String {
113 let mut string = String::with_capacity(SFN_LEN);
114 string.push_str(codepage.decode(&self.name).trim_end());
116
117 let ext = codepage.decode(&self.ext).trim_end().to_owned();
119 if !ext.is_empty() {
120 string.push_str(&ext);
121 };
122
123 string
124 }
125}
126
127#[derive(Clone, Debug)]
129pub struct Properties {
130 pub(crate) path: Box<Path>,
131 pub(crate) sfn: (Sfn, Codepage),
132 pub(crate) is_dir: bool,
133 pub(crate) attributes: Attributes,
134 pub(crate) created: Option<PrimitiveDateTime>,
135 pub(crate) modified: PrimitiveDateTime,
136 pub(crate) accessed: Option<Date>,
137 pub(crate) file_size: u32,
138 pub(crate) data_cluster: u32,
139
140 pub(crate) chain: DirEntryChain,
142}
143
144impl Properties {
146 #[inline]
147 pub fn path(&self) -> &Path {
149 &self.path
150 }
151
152 #[inline]
153 pub fn sfn(&self) -> String {
155 self.sfn.0.decode(&self.sfn.1)
156 }
157
158 #[inline]
159 pub fn is_dir(&self) -> bool {
161 self.is_dir
162 }
163
164 #[inline]
165 pub fn is_file(&self) -> bool {
167 !self.is_dir()
168 }
169
170 #[inline]
171 pub fn attributes(&self) -> &Attributes {
173 &self.attributes
174 }
175
176 #[inline]
177 pub fn creation_time(&self) -> &Option<PrimitiveDateTime> {
182 &self.created
183 }
184
185 #[inline]
186 pub fn modification_time(&self) -> &PrimitiveDateTime {
190 &self.modified
191 }
192
193 #[inline]
194 pub fn last_accessed_date(&self) -> &Option<Date> {
199 &self.accessed
200 }
201
202 #[inline]
203 pub fn file_size(&self) -> u32 {
207 self.file_size
208 }
209}
210
211impl Properties {
212 pub(crate) fn from_raw(raw_props: RawProperties, path: Box<Path>, codepage: Codepage) -> Self {
213 Self {
214 path,
215 sfn: (raw_props.sfn, codepage),
216 is_dir: raw_props.is_dir,
217 attributes: raw_props.attributes.into(),
218 created: raw_props.created,
219 modified: raw_props.modified,
220 accessed: raw_props.accessed,
221 file_size: raw_props.file_size,
222 data_cluster: raw_props.data_cluster,
223 chain: raw_props.chain,
224 }
225 }
226}
227
228#[derive(Debug)]
230pub struct DirEntry<'a, S>
231where
232 S: Read + Seek,
233{
234 pub(crate) entry: Properties,
235 pub(crate) fs: &'a FileSystem<S>,
236}
237
238impl<'a, S> DirEntry<'a, S>
239where
240 S: Read + Seek,
241{
242 pub fn to_ro_file(&self) -> Option<ROFile<'a, S>> {
246 self.is_file().then(|| ROFile {
247 fs: self.fs,
248 props: FileProps {
249 entry: self.entry.clone(),
250 offset: 0,
251 current_cluster: self.data_cluster,
252 },
253 })
254 }
255
256 pub fn to_dir(&self) -> Option<ReadDir<'a, S>> {
260 self.is_dir().then(|| {
261 ReadDir::new(
262 self.fs,
263 &EntryLocationUnit::DataCluster(self.data_cluster),
264 self.path(),
265 )
266 })
267 }
268}
269
270impl<'a, S> DirEntry<'a, S>
271where
272 S: Read + Write + Seek,
273{
274 pub fn to_rw_file(self) -> Option<RWFile<'a, S>> {
278 self.to_ro_file().map(|ro_file| ro_file.into())
279 }
280}
281
282impl<S> ops::Deref for DirEntry<'_, S>
283where
284 S: Read + Seek,
285{
286 type Target = Properties;
287
288 #[inline]
289 fn deref(&self) -> &Self::Target {
290 &self.entry
291 }
292}
293
294#[derive(Debug)]
299pub struct ReadDir<'a, S>
300where
301 S: Read + Seek,
302{
303 inner: ReadDirInt<'a, S>,
304 parent: Box<Path>,
305}
306
307impl<'a, S> ReadDir<'a, S>
308where
309 S: Read + Seek,
310{
311 pub(crate) fn new<P>(fs: &'a FileSystem<S>, chain_start: &EntryLocationUnit, parent: P) -> Self
312 where
313 P: AsRef<Path>,
314 {
315 Self {
316 inner: ReadDirInt::new(fs, chain_start),
317 parent: parent.as_ref().into(),
318 }
319 }
320}
321
322impl<'a, S> Iterator for ReadDir<'a, S>
323where
324 S: Read + Seek,
325{
326 type Item = Result<DirEntry<'a, S>, S::Error>;
327
328 fn next(&mut self) -> Option<Self::Item> {
329 loop {
330 match self.inner.next() {
331 Some(res) => match res {
332 Ok(value) => {
333 if self.inner.fs.filter.borrow().filter(&value)
334 && ![path_consts::CURRENT_DIR_STR, path_consts::PARENT_DIR_STR]
336 .contains(&value.name.as_str())
337 {
338 return Some(Ok(value.into_dir_entry(&self.parent, self.inner.fs)));
339 } else {
340 continue;
341 }
342 }
343 Err(err) => return Some(Err(err)),
344 },
345 None => return None,
346 }
347 }
348 }
349}