rlvgl_core/plugins/
fatfs.rs1use alloc::string::{String, ToString};
3use alloc::vec;
4use alloc::vec::Vec;
5use fatfs::{FileSystem, FsOptions};
6use fscommon::BufStream;
7use std::io::{Read, Seek, SeekFrom, Write};
8
9#[cfg(test)]
10use fatfs::FormatVolumeOptions;
11#[cfg(test)]
12use std::io::Cursor;
13
14pub fn list_dir<T>(mut image: T, dir: &str) -> std::io::Result<Vec<String>>
19where
20 T: Read + Write + Seek,
21{
22 image.seek(SeekFrom::Start(0))?;
23 let buf_stream = BufStream::new(image);
24 let fs = FileSystem::new(buf_stream, FsOptions::new())?;
25 let root = fs.root_dir();
26 let mut names = Vec::new();
27 if dir.is_empty() || dir == "/" {
28 for r in root.iter() {
29 let entry = r?;
30 names.push(entry.file_name().to_string());
31 }
32 } else {
33 let subdir = root.open_dir(dir)?;
34 for r in subdir.iter() {
35 let entry = r?;
36 names.push(entry.file_name().to_string());
37 }
38 }
39 Ok(names)
40}
41
42pub fn file_exists<T>(mut image: T, path: &str) -> std::io::Result<bool>
44where
45 T: Read + Write + Seek,
46{
47 image.seek(SeekFrom::Start(0))?;
48 let buf_stream = BufStream::new(image);
49 let fs = FileSystem::new(buf_stream, FsOptions::new())?;
50 Ok(fs.root_dir().open_file(path).is_ok())
51}
52
53pub fn read_file<T>(mut image: T, path: &str) -> std::io::Result<Vec<u8>>
55where
56 T: Read + Write + Seek,
57{
58 image.seek(SeekFrom::Start(0))?;
59 let buf_stream = BufStream::new(image);
60 let fs = FileSystem::new(buf_stream, FsOptions::new())?;
61 let mut file = fs.root_dir().open_file(path)?;
62 let mut data = Vec::new();
63 file.read_to_end(&mut data)?;
64 Ok(data)
65}
66
67pub fn read_file_range<T>(
72 mut image: T,
73 path: &str,
74 offset: u64,
75 len: usize,
76) -> std::io::Result<Vec<u8>>
77where
78 T: Read + Write + Seek,
79{
80 image.seek(SeekFrom::Start(0))?;
81 let buf_stream = BufStream::new(image);
82 let fs = FileSystem::new(buf_stream, FsOptions::new())?;
83 let mut file = fs.root_dir().open_file(path)?;
84 file.seek(SeekFrom::Start(offset))?;
85 let mut buf = vec![0u8; len];
86 let n = file.read(&mut buf)?;
87 buf.truncate(n);
88 Ok(buf)
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use std::io::{SeekFrom, Write};
95
96 #[test]
97 fn basic_file_ops() {
98 let mut img = Cursor::new(vec![0u8; 1024 * 512]);
99 fatfs::format_volume(&mut img, FormatVolumeOptions::new()).unwrap();
100 img.seek(SeekFrom::Start(0)).unwrap();
101 {
102 let buf_stream = BufStream::new(&mut img);
103 let fs = FileSystem::new(buf_stream, FsOptions::new()).unwrap();
104 fs.root_dir().create_dir("testdir").unwrap();
105 fs.root_dir()
106 .create_file("foo.txt")
107 .unwrap()
108 .write_all(b"hello")
109 .unwrap();
110 }
111 img.seek(SeekFrom::Start(0)).unwrap();
112 let names = list_dir(&mut img, "/").unwrap();
113 assert!(names.contains(&"testdir".to_string()));
114 assert!(names.contains(&"foo.txt".to_string()));
115
116 img.seek(SeekFrom::Start(0)).unwrap();
117 assert!(file_exists(&mut img, "foo.txt").unwrap());
118 img.seek(SeekFrom::Start(0)).unwrap();
119 assert!(!file_exists(&mut img, "missing.txt").unwrap());
120
121 img.seek(SeekFrom::Start(0)).unwrap();
122 let data = read_file(&mut img, "foo.txt").unwrap();
123 assert_eq!(data, b"hello");
124 }
125
126 #[test]
127 fn partial_read_seek() {
128 let mut img = Cursor::new(vec![0u8; 1024 * 512]);
129 fatfs::format_volume(&mut img, FormatVolumeOptions::new()).unwrap();
130 img.seek(SeekFrom::Start(0)).unwrap();
131 {
132 let buf_stream = BufStream::new(&mut img);
133 let fs = FileSystem::new(buf_stream, FsOptions::new()).unwrap();
134 let mut file = fs.root_dir().create_file("foo.bin").unwrap();
135 let data: Vec<u8> = (0u8..=255).collect();
136 file.write_all(&data).unwrap();
137 }
138
139 img.seek(SeekFrom::Start(0)).unwrap();
140 let slice = read_file_range(&mut img, "foo.bin", 10, 5).unwrap();
141 assert_eq!(slice, vec![10, 11, 12, 13, 14]);
142
143 img.seek(SeekFrom::Start(0)).unwrap();
144 let slice = read_file_range(&mut img, "foo.bin", 250, 10).unwrap();
145 assert_eq!(slice, vec![250, 251, 252, 253, 254, 255]);
146 }
147}