egui_file_dialog/
file_system.rs1use std::io::{self, Read};
2use std::path::{Path, PathBuf};
3
4use crate::data::{Disks, Metadata, UserDirectories};
5
6pub trait FileSystem {
34    fn metadata(&self, path: &Path) -> io::Result<Metadata>;
36
37    fn is_dir(&self, path: &Path) -> bool;
39
40    fn is_file(&self, path: &Path) -> bool;
42
43    fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>>;
45
46    fn get_disks(&self, canonicalize_paths: bool) -> Disks;
48
49    fn is_path_hidden(&self, path: &Path) -> bool;
51
52    fn create_dir(&self, path: &Path) -> io::Result<()>;
54
55    fn user_dirs(&self, canonicalize_paths: bool) -> Option<UserDirectories>;
57
58    fn current_dir(&self) -> io::Result<PathBuf>;
60
61    fn load_text_file_preview(&self, _path: &Path, _max_chars: usize) -> io::Result<String> {
63        Err(std::io::Error::new(
64            std::io::ErrorKind::Unsupported,
65            "load_text_file_preview not implemented.".to_string(),
66        ))
67    }
68}
69
70impl std::fmt::Debug for dyn FileSystem + Send + Sync {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        write!(f, "<FileSystem>")
73    }
74}
75
76pub struct NativeFileSystem;
78
79impl FileSystem for NativeFileSystem {
80    fn metadata(&self, path: &Path) -> io::Result<Metadata> {
81        let mut metadata = Metadata::default();
82
83        let md = std::fs::metadata(path)?;
84        metadata.size = Some(md.len());
85        metadata.last_modified = md.modified().ok();
86        metadata.created = md.created().ok();
87        metadata.file_type = Some(format!("{:?}", md.file_type()));
88
89        Ok(metadata)
90    }
91
92    fn is_dir(&self, path: &Path) -> bool {
93        path.is_dir()
94    }
95
96    fn is_file(&self, path: &Path) -> bool {
97        path.is_file()
98    }
99
100    fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
101        Ok(std::fs::read_dir(path)?
102            .filter_map(Result::ok)
103            .map(|entry| entry.path())
104            .collect())
105    }
106
107    fn load_text_file_preview(&self, path: &Path, max_chars: usize) -> io::Result<String> {
108        let mut file = std::fs::File::open(path)?;
109        let mut chunk = [0; 96]; let mut buffer = String::new();
111
112        let mut total_read = 0;
114
115        while total_read < max_chars {
117            let bytes_read = file.read(&mut chunk)?;
118            if bytes_read == 0 {
119                break; }
121            let chars_read: String = String::from_utf8(chunk[..bytes_read].to_vec())
122                .map_err(|_| io::Error::from(io::ErrorKind::InvalidData))?;
123            total_read += chars_read.len();
124            buffer.push_str(&chars_read);
125        }
126
127        Ok(buffer.to_string())
128    }
129
130    fn get_disks(&self, canonicalize_paths: bool) -> Disks {
131        Disks::new_native_disks(canonicalize_paths)
132    }
133
134    fn is_path_hidden(&self, path: &Path) -> bool {
135        is_path_hidden(path)
136    }
137
138    fn create_dir(&self, path: &Path) -> io::Result<()> {
139        std::fs::create_dir(path)
140    }
141
142    fn user_dirs(&self, canonicalize_paths: bool) -> Option<UserDirectories> {
143        if let Some(dirs) = directories::UserDirs::new() {
144            return Some(UserDirectories::new(
145                UserDirectories::canonicalize(Some(dirs.home_dir()), canonicalize_paths),
146                UserDirectories::canonicalize(dirs.audio_dir(), canonicalize_paths),
147                UserDirectories::canonicalize(dirs.desktop_dir(), canonicalize_paths),
148                UserDirectories::canonicalize(dirs.document_dir(), canonicalize_paths),
149                UserDirectories::canonicalize(dirs.download_dir(), canonicalize_paths),
150                UserDirectories::canonicalize(dirs.picture_dir(), canonicalize_paths),
151                UserDirectories::canonicalize(dirs.video_dir(), canonicalize_paths),
152            ));
153        }
154
155        None
156    }
157
158    fn current_dir(&self) -> io::Result<PathBuf> {
159        std::env::current_dir()
160    }
161}
162
163#[cfg(windows)]
164fn is_path_hidden(path: &Path) -> bool {
165    use std::os::windows::fs::MetadataExt;
166
167    std::fs::metadata(path).is_ok_and(|metadata| metadata.file_attributes() & 0x2 > 0)
168}
169
170#[cfg(not(windows))]
171fn is_path_hidden(path: &Path) -> bool {
172    let Some(file_name) = path.file_name() else {
173        return false;
174    };
175    let Some(s) = file_name.to_str() else {
176        return false;
177    };
178
179    if s.starts_with('.') {
180        return true;
181    }
182
183    false
184}