1use std::borrow::Cow;
2use serde::{Deserialize, Serialize};
3use crate::{Error, Result};
4
5#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
28#[serde(rename_all = "camelCase")]
29pub struct FileUri {
30 pub uri: String,
32
33 pub document_top_tree_uri: Option<String>,
35}
36
37impl FileUri {
38
39 pub fn to_string(&self) -> crate::Result<String> {
40 serde_json::to_string(self).map_err(Into::into)
41 }
42
43 pub fn from_str(s: &str) -> crate::Result<Self> {
44 serde_json::from_str(s).map_err(Into::into)
45 }
46}
47
48impl From<&std::path::PathBuf> for FileUri {
49
50 fn from(value: &std::path::PathBuf) -> Self {
51 Self { uri: format!("file://{}", value.to_string_lossy()), document_top_tree_uri: None }
52 }
53}
54
55impl From<std::path::PathBuf> for FileUri {
56
57 fn from(ref value: std::path::PathBuf) -> Self {
58 value.into()
59 }
60}
61
62impl From<tauri_plugin_fs::FilePath> for FileUri {
63
64 fn from(value: tauri_plugin_fs::FilePath) -> Self {
65 match value {
66 tauri_plugin_fs::FilePath::Url(url) => Self { uri: url.to_string(), document_top_tree_uri: None },
67 tauri_plugin_fs::FilePath::Path(path_buf) => path_buf.into(),
68 }
69 }
70}
71
72impl From<FileUri> for tauri_plugin_fs::FilePath {
73
74 fn from(value: FileUri) -> Self {
75 let result: std::result::Result<_, std::convert::Infallible> = value.uri.parse();
76
77 result.unwrap()
80 }
81}
82
83#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
84#[serde(rename_all = "camelCase")]
85pub struct PublicStorageVolume {
86
87 pub description: String,
96
97 pub is_primary: bool,
100
101 pub id: PublicStorageVolumeId,
102}
103
104#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
105#[serde(rename_all = "camelCase")]
106pub struct PublicStorageVolumeId(pub String);
107
108#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub enum Entry {
111
112 #[non_exhaustive]
113 File {
114 uri: FileUri,
115 name: String,
116 last_modified: std::time::SystemTime,
117 len: u64,
118 mime_type: String,
119 },
120
121 #[non_exhaustive]
122 Dir {
123 uri: FileUri,
124 name: String,
125 last_modified: std::time::SystemTime,
126 }
127}
128
129#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
130#[serde(rename_all = "camelCase")]
131pub enum EntryType {
132 File {
133 mime_type: String
134 },
135 Dir,
136}
137
138impl EntryType {
139
140 pub fn is_file(&self) -> bool {
141 matches!(self, Self::File { .. })
142 }
143
144 pub fn is_dir(&self) -> bool {
145 matches!(self, Self::Dir)
146 }
147
148 pub fn mime_type(&self) -> Option<&str> {
151 match self {
152 EntryType::File { mime_type } => Some(&mime_type),
153 EntryType::Dir => None,
154 }
155 }
156
157 pub fn into_mime_type(self) -> Option<String> {
160 match self {
161 EntryType::File { mime_type } => Some(mime_type),
162 EntryType::Dir => None,
163 }
164 }
165}
166
167#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
169pub enum PersistableAccessMode {
170
171 Read,
173
174 Write,
176
177 ReadAndWrite,
179}
180
181#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
182pub enum PersistedUriPermission {
183 File {
184 uri: FileUri,
185 can_read: bool,
186 can_write: bool,
187 },
188 Dir {
189 uri: FileUri,
190 can_read: bool,
191 can_write: bool,
192 }
193}
194
195impl PersistedUriPermission {
196
197 pub fn uri(&self) -> &FileUri {
198 match self {
199 PersistedUriPermission::File { uri, .. } => uri,
200 PersistedUriPermission::Dir { uri, .. } => uri,
201 }
202 }
203
204 pub fn can_read(&self) -> bool {
205 match self {
206 PersistedUriPermission::File { can_read, .. } => *can_read,
207 PersistedUriPermission::Dir { can_read, .. } => *can_read,
208 }
209 }
210
211 pub fn can_write(&self) -> bool {
212 match self {
213 PersistedUriPermission::File { can_write, .. } => *can_write,
214 PersistedUriPermission::Dir { can_write, .. } => *can_write,
215 }
216 }
217
218 pub fn is_file(&self) -> bool {
219 matches!(self, PersistedUriPermission::File { .. })
220 }
221
222 pub fn is_dir(&self) -> bool {
223 matches!(self, PersistedUriPermission::Dir { .. })
224 }
225}
226
227#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
228pub struct Size {
229 pub width: u32,
230 pub height: u32
231}
232
233#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
234#[non_exhaustive]
235pub enum ImageFormat {
236
237 Png,
240
241 Jpeg,
244
245 Webp,
248
249 JpegWith {
252
253 quality: f32
257 },
258
259 WebpWith {
262
263 quality: f32
267 }
268}
269
270#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
272#[non_exhaustive]
273pub enum FileAccessMode {
274
275 Read,
279
280 #[deprecated(note = "This may or may not truncate existing contents. If the new file is smaller than the old one, this may cause the file to become corrupted.")]
291 Write,
292
293 WriteTruncate,
298
299 WriteAppend,
304
305 ReadWrite,
309
310 ReadWriteTruncate,
315}
316
317impl FileAccessMode {
318
319 #[allow(unused)]
320 #[allow(deprecated)]
321 pub(crate) fn to_mode(&self) -> &'static str {
322 match self {
323 FileAccessMode::Read => "r",
324 FileAccessMode::Write => "w",
325 FileAccessMode::WriteTruncate => "wt",
326 FileAccessMode::WriteAppend => "wa",
327 FileAccessMode::ReadWriteTruncate => "rwt",
328 FileAccessMode::ReadWrite => "rw",
329 }
330 }
331
332 #[allow(unused)]
333 #[allow(deprecated)]
334 pub(crate) fn from_mode(mode: &str) -> Result<Self> {
335 match mode {
336 "r" => Ok(Self::Read),
337 "w" => Ok(Self::Write),
338 "wt" => Ok(Self::WriteTruncate),
339 "wa" => Ok(Self::WriteAppend),
340 "rwt" => Ok(Self::ReadWriteTruncate),
341 "rw" => Ok(Self::ReadWrite),
342 mode => Err(Error { msg: format!("Illegal mode: {mode}").into() })
343 }
344 }
345}
346
347#[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)]
349#[non_exhaustive]
350pub enum VisualMediaTarget {
351
352 ImageOnly,
354
355 VideoOnly,
357
358 ImageAndVideo,
360}
361
362#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
364#[non_exhaustive]
365pub enum PrivateDir {
366
367 Data,
376
377 Cache,
387}
388
389#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
390#[non_exhaustive]
391pub enum PublicDir {
392
393 #[serde(untagged)]
394 Image(PublicImageDir),
395
396 #[serde(untagged)]
397 Video(PublicVideoDir),
398
399 #[serde(untagged)]
400 Audio(PublicAudioDir),
401
402 #[serde(untagged)]
403 GeneralPurpose(PublicGeneralPurposeDir),
404}
405
406#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
408#[non_exhaustive]
409pub enum PublicImageDir {
410
411 Pictures,
415
416 DCIM,
420}
421
422#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
424#[non_exhaustive]
425pub enum PublicVideoDir {
426
427 Movies,
431
432 DCIM,
436}
437
438#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
440#[non_exhaustive]
441pub enum PublicAudioDir {
442
443 Music,
447
448 Alarms,
452
453 Audiobooks,
459
460 Notifications,
464
465 Podcasts,
469
470 Ringtones,
474
475 Recordings,
481}
482
483#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
485#[non_exhaustive]
486pub enum PublicGeneralPurposeDir {
487
488 Documents,
492
493 Download,
497}
498
499impl std::fmt::Display for PublicImageDir {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 match self {
502 PublicImageDir::Pictures => write!(f, "Pictures"),
503 PublicImageDir::DCIM => write!(f, "DCIM"),
504 }
505 }
506}
507
508impl std::fmt::Display for PublicVideoDir {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 match self {
511 PublicVideoDir::Movies => write!(f, "Movies"),
512 PublicVideoDir::DCIM => write!(f, "DCIM"),
513 }
514 }
515}
516
517impl std::fmt::Display for PublicAudioDir {
518 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
519 match self {
520 PublicAudioDir::Music => write!(f, "Music"),
521 PublicAudioDir::Alarms => write!(f, "Alarms"),
522 PublicAudioDir::Audiobooks => write!(f, "Audiobooks"),
523 PublicAudioDir::Notifications => write!(f, "Notifications"),
524 PublicAudioDir::Podcasts => write!(f, "Podcasts"),
525 PublicAudioDir::Ringtones => write!(f, "Ringtones"),
526 PublicAudioDir::Recordings => write!(f, "Recordings"),
527 }
528 }
529}
530
531impl std::fmt::Display for PublicGeneralPurposeDir {
532 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
533 match self {
534 PublicGeneralPurposeDir::Documents => write!(f, "Documents"),
535 PublicGeneralPurposeDir::Download => write!(f, "Download"),
536 }
537 }
538}
539
540impl std::fmt::Display for PublicDir {
541 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
542 match self {
543 PublicDir::Image(p) => p.fmt(f),
544 PublicDir::Video(p) => p.fmt(f),
545 PublicDir::Audio(p) => p.fmt(f),
546 PublicDir::GeneralPurpose(p) => p.fmt(f),
547 }
548 }
549}
550
551macro_rules! impl_into_pubdir {
552 ($target: ident, $wrapper: ident) => {
553 impl From<$target> for PublicDir {
554 fn from(value: $target) -> Self {
555 Self::$wrapper(value)
556 }
557 }
558 };
559}
560impl_into_pubdir!(PublicImageDir, Image);
561impl_into_pubdir!(PublicVideoDir, Video);
562impl_into_pubdir!(PublicAudioDir, Audio);
563impl_into_pubdir!(PublicGeneralPurposeDir, GeneralPurpose);
564
565#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
566#[non_exhaustive]
567pub enum InitialLocation<'a> {
568
569 PrimaryTopDir,
570
571 PrimaryPublicDir {
572 dir: PublicDir,
573 relative_path: &'a str,
574 },
575
576 PrimaryPublicAppDir {
577 dir: PublicDir,
578 relative_path: &'a str,
579 },
580
581 TopDir {
582 volume: &'a PublicStorageVolumeId,
583 },
584
585 PublicDir {
586 volume: &'a PublicStorageVolumeId,
587 dir: PublicDir,
588 relative_path: &'a str,
589 },
590
591 PublicAppDir {
592 volume: &'a PublicStorageVolumeId,
593 dir: PublicDir,
594 relative_path: &'a str,
595 }
596}
597
598impl<'a> InitialLocation<'a> {
599
600 #[allow(unused)]
601 pub(crate) fn volume(&'a self) -> Option<&'a PublicStorageVolumeId> {
602 match self {
603 InitialLocation::PrimaryTopDir => None,
604 InitialLocation::PrimaryPublicDir { .. } => None,
605 InitialLocation::PrimaryPublicAppDir { .. } => None,
606 InitialLocation::TopDir { volume } => Some(volume),
607 InitialLocation::PublicDir { volume, .. } => Some(volume),
608 InitialLocation::PublicAppDir { volume, .. } => Some(volume),
609 }
610 }
611
612 #[allow(unused)]
613 pub(crate) fn dir_and_relative_path(&'a self, app_dir: &'a str) -> Option<(PublicDir, Cow<'a, str>)> {
614 let (dir, use_app_dir, realtive_path) = match self {
615 InitialLocation::PrimaryTopDir => return None,
616 InitialLocation::TopDir { .. } => return None,
617 InitialLocation::PrimaryPublicDir { dir, relative_path } => (dir, false, relative_path),
618 InitialLocation::PublicDir { dir, relative_path, .. } => (dir, false, relative_path),
619 InitialLocation::PrimaryPublicAppDir { dir, relative_path } => (dir, true, relative_path),
620 InitialLocation::PublicAppDir { dir, relative_path, .. } => (dir, true, relative_path),
621 };
622
623 let relative_path = realtive_path.trim_matches('/');
624 let app_dir = app_dir.trim_matches('/');
625
626 if use_app_dir {
627 Some((*dir, Cow::Owned(format!("{app_dir}/{realtive_path}"))))
628 }
629 else {
630 Some((*dir, Cow::Borrowed(relative_path)))
631 }
632 }
633}