egui_file_dialog/data/
disks.rs1#[cfg(target_os = "macos")]
2use std::fs;
3use std::path::{Path, PathBuf};
4
5#[derive(Default, Debug, Clone, PartialEq, Eq)]
9pub struct Disk {
10 mount_point: PathBuf,
11 display_name: String,
12 is_removable: bool,
13}
14
15impl Disk {
16 pub fn new(
18 name: Option<&str>,
19 mount_point: &Path,
20 is_removable: bool,
21 canonicalize_paths: bool,
22 ) -> Self {
23 Self {
24 mount_point: canonicalize(mount_point, canonicalize_paths),
25 display_name: gen_display_name(
26 name.unwrap_or_default(),
27 mount_point.to_str().unwrap_or_default(),
28 ),
29 is_removable,
30 }
31 }
32
33 pub fn from_sysinfo_disk(disk: &sysinfo::Disk, canonicalize_paths: bool) -> Self {
35 Self::new(
36 disk.name().to_str(),
37 disk.mount_point(),
38 disk.is_removable(),
39 canonicalize_paths,
40 )
41 }
42
43 #[cfg(target_os = "macos")]
45 pub fn from_path(path: &Path, canonicalize_paths: bool) -> Self {
46 let mount_point = canonicalize(path, canonicalize_paths);
47
48 let display_name = path.file_name().map_or_else(
50 || "Unknown".to_string(),
51 |name| name.to_string_lossy().to_string(),
52 );
53
54 let is_removable = false; Self {
59 mount_point,
60 display_name,
61 is_removable,
62 }
63 }
64
65 pub fn mount_point(&self) -> &Path {
67 &self.mount_point
68 }
69
70 pub fn display_name(&self) -> &str {
72 &self.display_name
73 }
74
75 pub const fn is_removable(&self) -> bool {
77 self.is_removable
78 }
79}
80
81#[derive(Default, Debug)]
83pub struct Disks {
84 disks: Vec<Disk>,
85}
86
87impl Disks {
88 pub const fn new(disks: Vec<Disk>) -> Self {
90 Self { disks }
91 }
92
93 pub fn new_native_disks(canonicalize_paths: bool) -> Self {
95 Self {
96 disks: load_disks(canonicalize_paths),
97 }
98 }
99
100 pub const fn new_empty() -> Self {
102 Self { disks: Vec::new() }
103 }
104
105 pub(crate) fn iter(&self) -> std::slice::Iter<'_, Disk> {
108 self.disks.iter()
109 }
110}
111
112impl<'a> IntoIterator for &'a Disks {
113 type IntoIter = std::slice::Iter<'a, Disk>;
114 type Item = &'a Disk;
115 fn into_iter(self) -> Self::IntoIter {
116 self.iter()
117 }
118}
119
120fn canonicalize(path: &Path, canonicalize: bool) -> PathBuf {
123 if canonicalize {
124 dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
125 } else {
126 path.to_path_buf()
127 }
128}
129
130#[cfg(windows)]
131fn gen_display_name(name: &str, mount_point: &str) -> String {
132 let mount_point = mount_point.replace('\\', "");
133
134 if name.is_empty() {
137 return mount_point;
138 }
139
140 format!("{name} ({mount_point})")
141}
142
143#[cfg(not(windows))]
144fn gen_display_name(name: &str, mount_point: &str) -> String {
145 if name.is_empty() {
148 return mount_point.to_string();
149 }
150
151 name.to_string()
152}
153
154#[cfg(windows)]
155fn load_disks(canonicalize_paths: bool) -> Vec<Disk> {
156 let mut disks: Vec<Disk> = sysinfo::Disks::new_with_refreshed_list()
157 .iter()
158 .map(|d| Disk::from_sysinfo_disk(d, canonicalize_paths))
159 .collect();
160
161 #[allow(unsafe_code)]
165 let mut drives = unsafe { GetLogicalDrives() };
166 let mut letter = b'A';
167
168 while drives > 0 {
169 if drives & 1 != 0 {
170 let path = PathBuf::from(format!("{}:\\", letter as char));
171 let mount_point = canonicalize(&path, canonicalize_paths);
172
173 if !disks.iter().any(|d| d.mount_point == mount_point) {
174 disks.push(Disk::new(None, &path, false, canonicalize_paths));
175 }
176 }
177
178 drives >>= 1;
179 letter += 1;
180 }
181
182 disks
183}
184
185#[cfg(windows)]
186extern "C" {
187 pub fn GetLogicalDrives() -> u32;
188}
189
190#[cfg(all(not(windows), not(target_os = "macos")))]
191fn load_disks(canonicalize_paths: bool) -> Vec<Disk> {
192 sysinfo::Disks::new_with_refreshed_list()
193 .iter()
194 .map(|d| Disk::from_sysinfo_disk(d, canonicalize_paths))
195 .collect()
196}
197
198#[cfg(target_os = "macos")]
200fn load_disks(canonicalize_paths: bool) -> Vec<Disk> {
201 let mut result = Vec::new();
202 let mut seen_mount_points = std::collections::HashSet::new();
203
204 for disk in &sysinfo::Disks::new_with_refreshed_list() {
206 let mount_point = disk.mount_point();
207 if mount_point != Path::new("/")
208 && seen_mount_points.insert(mount_point.to_path_buf())
209 && disk.mount_point() != Path::new("/System/Volumes/Data")
210 {
211 result.push(Disk::from_sysinfo_disk(disk, canonicalize_paths));
212 }
213 }
214
215 if let Ok(entries) = fs::read_dir("/Volumes") {
217 for entry in entries.filter_map(Result::ok) {
218 let path = entry.path();
219 if seen_mount_points.insert(path.clone()) {
220 if let Some(name_osstr) = path.file_name() {
221 if let Some(name) = name_osstr.to_str() {
222 if path.is_dir() && !name.starts_with('.') {
223 result.push(Disk::from_path(&path, canonicalize_paths));
224 }
225 }
226 }
227 }
228 }
229 }
230
231 result
232}