gui_server_api/navigation/
filepicker.rs1#[derive(Debug, Copy, Clone, Eq, PartialEq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
7pub enum Location {
8 Internal,
9 Airlock,
10 External,
11}
12
13#[derive(Debug, Clone, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
14pub enum AllowedLocations {
15 All,
16 Specific(Vec<Location>),
17}
18
19impl AllowedLocations {
20 pub fn specific<T: IntoIterator<Item = Location>>(locations: T) -> Self {
21 Self::Specific(locations.into_iter().collect::<Vec<_>>())
22 }
23
24 pub fn contains(&self, location: Location) -> bool {
25 match self {
26 Self::All => true,
27 Self::Specific(locations) => locations.contains(&location),
28 }
29 }
30
31 pub fn len(&self) -> Option<usize> {
32 match self {
33 Self::All => None,
34 Self::Specific(locations) => Some(locations.len()),
35 }
36 }
37
38 pub fn is_empty(&self) -> bool { self.len().map(|len| len == 0).unwrap_or(false) }
39}
40
41#[derive(Debug, Clone, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
42pub enum AllowedExtensions {
43 All,
44 Specific(Vec<String>),
45}
46
47impl AllowedExtensions {
48 pub fn specific<S: AsRef<str>, T: IntoIterator<Item = S>>(extensions: T) -> Self {
49 Self::Specific(extensions.into_iter().map(|s| s.as_ref().to_string()).collect::<Vec<_>>())
50 }
51
52 pub fn contains<S: AsRef<str>>(&self, extension: S) -> bool {
53 match self {
54 Self::All => true,
55 Self::Specific(extensions) => {
56 extensions.iter().any(|allowed| allowed.eq_ignore_ascii_case(extension.as_ref()))
57 }
58 }
59 }
60}
61
62#[derive(Debug, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
74pub struct SelectFileOptions {
75 start_location: Location,
76 locations: AllowedLocations,
77 extensions: AllowedExtensions,
78 hidden_allowed: bool,
79 dirs_allowed: bool,
80 search_allowed: bool,
81 dir_selection_mode: bool,
82 multiple_selection_mode: bool,
83}
84
85impl Default for SelectFileOptions {
86 fn default() -> Self {
87 Self {
88 start_location: Location::Internal,
89 locations: AllowedLocations::All,
90 extensions: AllowedExtensions::All,
91 hidden_allowed: true,
92 search_allowed: true,
93 dirs_allowed: true,
94 dir_selection_mode: false,
95 multiple_selection_mode: false,
96 }
97 }
98}
99
100impl SelectFileOptions {
101 pub fn with_dir_selection_mode(self, dir_selection_mode: bool) -> Self {
106 Self { dir_selection_mode, dirs_allowed: self.dirs_allowed || dir_selection_mode, ..self }
107 }
108
109 pub fn with_dirs_allowed(self, allow_directories: bool) -> Self {
113 Self { dirs_allowed: allow_directories || self.dir_selection_mode, ..self }
114 }
115
116 pub fn with_search_allowed(self, allow_search: bool) -> Self {
118 Self { search_allowed: allow_search, ..self }
119 }
120
121 pub fn with_multiple_selection_mode(self, multiple_selection_mode: bool) -> Self {
125 let dir_selection_mode = self.dir_selection_mode && !multiple_selection_mode;
126 Self { multiple_selection_mode, dir_selection_mode, ..self }
127 }
128
129 pub fn with_hidden_allowed(self, hidden_allowed: bool) -> Self { Self { hidden_allowed, ..self } }
131
132 pub fn with_start_location(self, start_location: Location) -> Self { Self { start_location, ..self } }
133
134 pub fn with_allowed_locations(self, locations: AllowedLocations) -> Self { Self { locations, ..self } }
135
136 pub fn with_allowed_extensions(self, extensions: AllowedExtensions) -> Self {
137 Self { extensions, ..self }
138 }
139
140 pub fn search_allowed(&self) -> bool { self.search_allowed }
141
142 pub fn dirs_allowed(&self) -> bool { self.dirs_allowed }
143
144 pub fn hidden_allowed(&self) -> bool { self.hidden_allowed }
145
146 pub fn dir_selection_mode(&self) -> bool { self.dir_selection_mode }
147
148 pub fn multiple_selection_mode(&self) -> bool { self.multiple_selection_mode }
149
150 pub fn start_location(&self) -> Location { self.start_location }
151
152 pub fn allowed_locations(&self) -> &AllowedLocations { &self.locations }
153
154 pub fn allowed_extensions(&self) -> &AllowedExtensions { &self.extensions }
155
156 pub fn from_slice(data: &[u8]) -> Option<Self> {
157 let Ok(archived) = rkyv::access::<ArchivedSelectFileOptions, rkyv::rancor::Error>(data) else {
158 return None;
159 };
160 rkyv::deserialize::<Self, rkyv::rancor::Error>(archived).ok()
161 }
162
163 pub fn serialize(&self) -> Vec<u8> { rkyv::to_bytes::<rkyv::rancor::Error>(self).unwrap().to_vec() }
164}
165
166#[derive(Debug, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
167pub struct SelectFileResult {
168 pub files: Vec<(String, Location)>,
169}
170
171impl SelectFileResult {
172 pub fn new(files: Vec<(String, Location)>) -> Self { Self { files } }
173
174 pub fn files(&self) -> &[(String, Location)] { &self.files }
175
176 pub fn from_slice(data: &[u8]) -> Option<Self> {
177 let Ok(archived) = rkyv::access::<ArchivedSelectFileResult, rkyv::rancor::Error>(data) else {
178 return None;
179 };
180 rkyv::deserialize::<Self, rkyv::rancor::Error>(archived).ok()
181 }
182
183 pub fn serialize(&self) -> Vec<u8> { rkyv::to_bytes::<rkyv::rancor::Error>(self).unwrap().to_vec() }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn allowed_extensions_match_case_insensitively() {
192 let extensions = AllowedExtensions::specific(["png", "jpg"]);
193
194 assert!(extensions.contains("JPG"));
195 assert!(extensions.contains("Png"));
196 }
197}