Skip to main content

rlobkit_dialogs/
picker.rs

1use crate::{RlobKitMode, RlobKitType};
2use rlobkit_core::{PlatformDirectory, PlatformFile, RlobKitError};
3use std::path::{Path, PathBuf};
4
5#[derive(Debug, Clone, Default)]
6pub struct OpenFileOptions {
7    pub file_type: RlobKitType,
8    pub mode: RlobKitMode,
9    pub title: Option<String>,
10    pub initial_directory: Option<PathBuf>,
11}
12
13#[derive(Debug, Clone, Default)]
14pub struct SaveFileOptions {
15    pub suggested_name: Option<String>,
16    pub extension: Option<String>,
17    pub title: Option<String>,
18    pub initial_directory: Option<PathBuf>,
19    pub file_type: Option<RlobKitType>,
20    #[cfg(target_arch = "wasm32")]
21    pub data: Option<Vec<u8>>,
22}
23
24#[derive(Debug, Clone, Default)]
25pub struct OpenDirectoryOptions {
26    pub title: Option<String>,
27    pub initial_directory: Option<PathBuf>,
28}
29
30trait PlatformBackend {
31    async fn open_file_picker(opts: OpenFileOptions) -> Result<Option<Vec<PlatformFile>>, RlobKitError>;
32    async fn open_directory_picker(opts: OpenDirectoryOptions) -> Result<Option<PlatformDirectory>, RlobKitError>;
33    async fn open_file_saver(opts: SaveFileOptions) -> Result<Option<PlatformFile>, RlobKitError>;
34    fn write_file_from_path(target: &PlatformFile, source: &Path) -> Result<(), RlobKitError>;
35    fn read_file_to_path(source: &PlatformFile, dest: &Path) -> Result<(), RlobKitError>;
36}
37
38#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
39struct Backend;
40
41#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
42impl PlatformBackend for Backend {
43    async fn open_file_picker(opts: OpenFileOptions) -> Result<Option<Vec<PlatformFile>>, RlobKitError> {
44        crate::desktop::open_file_picker(opts).await
45    }
46
47    async fn open_directory_picker(opts: OpenDirectoryOptions) -> Result<Option<PlatformDirectory>, RlobKitError> {
48        crate::desktop::open_directory_picker(opts).await
49    }
50
51    async fn open_file_saver(opts: SaveFileOptions) -> Result<Option<PlatformFile>, RlobKitError> {
52        crate::desktop::open_file_saver(opts).await
53    }
54
55    fn write_file_from_path(target: &PlatformFile, source: &Path) -> Result<(), RlobKitError> {
56        crate::desktop::write_file_from_path(target, source)
57    }
58
59    fn read_file_to_path(source: &PlatformFile, dest: &Path) -> Result<(), RlobKitError> {
60        crate::desktop::read_file_to_path(source, dest)
61    }
62}
63
64#[cfg(target_arch = "wasm32")]
65struct Backend;
66
67#[cfg(target_arch = "wasm32")]
68impl PlatformBackend for Backend {
69    async fn open_file_picker(opts: OpenFileOptions) -> Result<Option<Vec<PlatformFile>>, RlobKitError> {
70        crate::wasm::open_file_picker(opts).await
71    }
72
73    async fn open_directory_picker(_opts: OpenDirectoryOptions) -> Result<Option<PlatformDirectory>, RlobKitError> {
74        Err(RlobKitError::UnsupportedOperation("Directory picker not supported on WASM".into()))
75    }
76
77    async fn open_file_saver(opts: SaveFileOptions) -> Result<Option<PlatformFile>, RlobKitError> {
78        crate::wasm::open_file_saver(opts).await
79    }
80
81    fn write_file_from_path(_target: &PlatformFile, _source: &Path) -> Result<(), RlobKitError> {
82        Err(RlobKitError::UnsupportedOperation("Filesystem copy is not supported on WASM".into()))
83    }
84
85    fn read_file_to_path(_source: &PlatformFile, _dest: &Path) -> Result<(), RlobKitError> {
86        Err(RlobKitError::UnsupportedOperation("Filesystem copy is not supported on WASM".into()))
87    }
88}
89
90#[cfg(target_os = "android")]
91struct Backend;
92
93#[cfg(target_os = "android")]
94impl PlatformBackend for Backend {
95    async fn open_file_picker(opts: OpenFileOptions) -> Result<Option<Vec<PlatformFile>>, RlobKitError> {
96        crate::android::open_file_picker(opts).await
97    }
98
99    async fn open_directory_picker(opts: OpenDirectoryOptions) -> Result<Option<PlatformDirectory>, RlobKitError> {
100        crate::android::open_directory_picker(opts).await
101    }
102
103    async fn open_file_saver(opts: SaveFileOptions) -> Result<Option<PlatformFile>, RlobKitError> {
104        crate::android::open_file_saver(opts).await
105    }
106
107    fn write_file_from_path(target: &PlatformFile, source: &Path) -> Result<(), RlobKitError> {
108        crate::android::write_file_from_path(target, source)
109    }
110
111    fn read_file_to_path(source: &PlatformFile, dest: &Path) -> Result<(), RlobKitError> {
112        crate::android::read_file_to_path(source, dest)
113    }
114}
115
116pub struct RlobKit;
117
118impl RlobKit {
119    pub async fn open_file_picker(
120        opts: OpenFileOptions,
121    ) -> Result<Option<Vec<PlatformFile>>, RlobKitError> {
122        Backend::open_file_picker(opts).await
123    }
124
125    pub async fn open_single_file(
126        file_type: RlobKitType,
127    ) -> Result<Option<PlatformFile>, RlobKitError> {
128        let result = Self::open_file_picker(OpenFileOptions {
129            file_type,
130            mode: RlobKitMode::Single,
131            ..Default::default()
132        })
133        .await?;
134        Ok(result.and_then(|v| v.into_iter().next()))
135    }
136
137    pub async fn open_directory_picker(
138        opts: OpenDirectoryOptions,
139    ) -> Result<Option<PlatformDirectory>, RlobKitError> {
140        Backend::open_directory_picker(opts).await
141    }
142
143    pub async fn open_file_saver(
144        opts: SaveFileOptions,
145    ) -> Result<Option<PlatformFile>, RlobKitError> {
146        Backend::open_file_saver(opts).await
147    }
148
149    pub fn write_file_from_path(
150        target: &PlatformFile,
151        source_path: &Path,
152    ) -> Result<(), RlobKitError> {
153        Backend::write_file_from_path(target, source_path)
154    }
155
156    pub fn read_file_to_path(source: &PlatformFile, dest_path: &Path) -> Result<(), RlobKitError> {
157        Backend::read_file_to_path(source, dest_path)
158    }
159
160    pub async fn save_bytes(
161        opts: SaveFileOptions,
162        data: &[u8],
163    ) -> Result<Option<PlatformFile>, RlobKitError> {
164        #[cfg(target_os = "android")]
165        {
166            let target = Self::open_file_saver(opts).await?;
167            if let Some(file) = &target {
168                file.write_bytes(data)?;
169            }
170            return Ok(target);
171        }
172
173        #[cfg(target_arch = "wasm32")]
174        {
175            let mut wasm_opts = opts;
176            wasm_opts.data = Some(data.to_vec());
177            return Self::open_file_saver(wasm_opts).await;
178        }
179
180        #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
181        {
182            let target = Self::open_file_saver(opts).await?;
183            if let Some(file) = &target {
184                file.write_bytes(data)?;
185            }
186            return Ok(target);
187        }
188
189        #[allow(unreachable_code)]
190        Err(RlobKitError::UnsupportedOperation(
191            "Unsupported platform".into(),
192        ))
193    }
194}