tauri_plugin_android_fs/api/
private_storage.rs

1use crate::*;
2
3
4/// API of file storage intended for the app’s use only.  
5/// 
6/// # Examples
7/// ```
8/// fn example(app: &tauri::AppHandle) {
9///     use tauri_plugin_android_fs::AndroidFsExt as _;
10/// 
11///     let api = app.android_fs();
12///     let private_storage = api.private_storage();
13/// }
14/// ```
15pub struct PrivateStorage<'a, R: tauri::Runtime>(
16    #[allow(unused)]
17    pub(crate) &'a AndroidFs<R>
18);
19
20impl<'a, R: tauri::Runtime> PrivateStorage<'a, R> {
21
22    /// Get the absolute path of the specified directory.  
23    /// App can fully manage entries within this directory without any permission via std::fs.   
24    ///
25    /// These files will be deleted when the app is uninstalled and may also be deleted at the user’s initialising request.  
26    /// When using [`PrivateDir::Cache`], the system will automatically delete files in this directory as disk space is needed elsewhere on the device.   
27    /// 
28    /// The returned path may change over time if the calling app is moved to an adopted storage device, 
29    /// so only relative paths should be persisted.   
30    /// 
31    /// # Examples
32    /// ```no_run
33    /// use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt, PrivateDir, PrivateStorage};
34    /// 
35    /// fn example(app: tauri::AppHandle) {
36    ///     let api = app.android_fs().private_storage();
37    /// 
38    ///     let dir_path = api.resolve_path(PrivateDir::Data).unwrap();
39    ///     let file_path = dir_path.join("2025-2-12/data.txt");
40    ///     
41    ///     // Write file
42    ///     std::fs::create_dir_all(file_path.parent().unwrap()).unwrap();
43    ///     std::fs::write(&file_path, "aaaa").unwrap();
44    /// 
45    ///     // Read file
46    ///     let _ = std::fs::read_to_string(&file_path).unwrap();
47    /// 
48    ///     // Remove file
49    ///     std::fs::remove_file(&file_path).unwrap();
50    /// }
51    /// ```
52    /// 
53    /// # Support
54    /// All Android version.
55    pub fn resolve_path(&self, dir: PrivateDir) -> crate::Result<std::path::PathBuf> {
56        on_android!({
57            impl_de!(struct Paths { data: String, cache: String });
58        
59            static PATHS: std::sync::OnceLock<Paths> = std::sync::OnceLock::new();
60
61            if PATHS.get().is_none() {
62                let paths = self.0.api
63                    .run_mobile_plugin::<Paths>("getPrivateBaseDirAbsolutePaths", "")?;
64
65                // The cell is guaranteed to contain a value when set returns
66                let _ = PATHS.set(paths);
67            }
68
69            let paths = PATHS.get().unwrap();
70
71            Ok(match dir {
72                PrivateDir::Data => std::path::PathBuf::from(paths.data.to_owned()),
73                PrivateDir::Cache => std::path::PathBuf::from(paths.cache.to_owned()),
74            })
75        })
76    }
77
78    /// Get the absolute path of the specified relative path and base directory.  
79    /// App can fully manage entries of this path without any permission via std::fs.   
80    ///
81    /// See [`PrivateStorage::resolve_path`] for details.  
82    /// 
83    /// # Support
84    /// All Android version.
85    pub fn resolve_path_with(
86        &self,
87        dir: PrivateDir,
88        relative_path: impl AsRef<str>
89    ) -> crate::Result<std::path::PathBuf> {
90
91        on_android!({
92            let relative_path = relative_path.as_ref().trim_start_matches('/');
93            let path = self.resolve_path(dir)?.join(relative_path);
94            Ok(path)
95        })
96    }
97
98    pub fn resolve_uri(&self, dir: PrivateDir) -> crate::Result<FileUri> {
99        on_android!({
100            self.resolve_path(dir).map(Into::into)
101        })
102    }
103
104    pub fn resolve_uri_with(&self, dir: PrivateDir, relative_path: impl AsRef<str>) -> crate::Result<FileUri> {
105        on_android!({
106            self.resolve_path_with(dir, relative_path).map(Into::into)
107        })
108    }
109
110    /// Writes a slice as the entire contents of a file.  
111    /// 
112    /// This function will create a file if it does not exist, and will entirely replace its contents if it does.  
113    /// Recursively create parent directories if they are missing.  
114    /// 
115    /// This internally uses [`PrivateStorage::resolve_path`] , [`std::fs::create_dir_all`], and [`std::fs::write`].  
116    /// See [`PrivateStorage::resolve_path`] for details.  
117    /// 
118    /// # Support
119    /// All Android version.
120    pub fn write(
121        &self, 
122        base_dir: PrivateDir, 
123        relative_path: impl AsRef<str>, 
124        contents: impl AsRef<[u8]>
125    ) -> crate::Result<()> {
126
127        on_android!({
128            let path = self.resolve_path_with(base_dir, relative_path)?;
129
130            if let Some(parent_dir) = path.parent() {
131                std::fs::create_dir_all(parent_dir)?;
132            }
133
134            std::fs::write(path, contents)?;
135            Ok(())
136        })
137    }
138
139    /// Open a file in read-only mode.  
140    /// 
141    /// If you only need to read the entire file contents, consider using [`PrivateStorage::read`]  or [`PrivateStorage::read_to_string`] instead.  
142    /// 
143    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::open`].  
144    /// See [`PrivateStorage::resolve_path`] for details.  
145    /// 
146    /// # Support
147    /// All Android version.
148    pub fn open_file(
149        &self,
150        base_dir: PrivateDir, 
151        relative_path: impl AsRef<str>, 
152    ) -> crate::Result<std::fs::File> {
153
154        on_android!({
155            let path = self.resolve_path_with(base_dir, relative_path)?;
156            Ok(std::fs::File::open(path)?)
157        })
158    }
159
160    /// Opens a file in write-only mode.  
161    /// This function will create a file if it does not exist, and will truncate it if it does.
162    /// 
163    /// If you only need to write the contents, consider using [`PrivateStorage::write`]  instead.  
164    /// 
165    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::create`].  
166    /// See [`PrivateStorage::resolve_path`] for details.  
167    /// 
168    /// # Support
169    /// All Android version.
170    pub fn create_file(
171        &self,
172        base_dir: PrivateDir, 
173        relative_path: impl AsRef<str>, 
174    ) -> crate::Result<std::fs::File> {
175
176        on_android!({
177            let path = self.resolve_path_with(base_dir, relative_path)?;
178            Ok(std::fs::File::create(path)?)
179        })
180    }
181
182    /// Creates a new file in read-write mode; error if the file exists. 
183    /// 
184    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::create_new`].  
185    /// See [`PrivateStorage::resolve_path`] for details.  
186    /// 
187    /// # Support
188    /// All Android version.
189    pub fn create_new_file(
190        &self,
191        base_dir: PrivateDir, 
192        relative_path: impl AsRef<str>, 
193    ) -> crate::Result<std::fs::File> {
194
195        on_android!({
196            let path = self.resolve_path_with(base_dir, relative_path)?;
197            Ok(std::fs::File::create_new(path)?)
198        })
199    }
200
201    /// Reads the entire contents of a file into a bytes vector.  
202    /// 
203    /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.  
204    /// 
205    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read`].  
206    /// See [`PrivateStorage::resolve_path`] for details.  
207    /// 
208    /// # Support
209    /// All Android version.
210    pub fn read(
211        &self,
212        base_dir: PrivateDir, 
213        relative_path: impl AsRef<str>, 
214    ) -> crate::Result<Vec<u8>> {
215
216        on_android!({
217            let path = self.resolve_path_with(base_dir, relative_path)?;
218            Ok(std::fs::read(path)?)
219        })
220    }
221
222    /// Reads the entire contents of a file into a string.  
223    /// 
224    /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.  
225    /// 
226    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_to_string`].  
227    /// See [`PrivateStorage::resolve_path`] for details.  
228    /// 
229    /// # Support
230    /// All Android version.
231    pub fn read_to_string(
232        &self,
233        base_dir: PrivateDir,
234        relative_path: impl AsRef<str>, 
235    ) -> crate::Result<String> {
236
237        on_android!({
238            let path = self.resolve_path_with(base_dir, relative_path)?;
239            Ok(std::fs::read_to_string(path)?)
240        })
241    }
242
243    /// Returns an iterator over the entries within a directory.
244    /// 
245    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_dir`].  
246    /// See [`PrivateStorage::resolve_path`] for details.  
247    /// 
248    /// # Support
249    /// All Android version.
250    pub fn read_dir(
251        &self,
252        base_dir: PrivateDir,
253        relative_path: Option<&str>,
254    ) -> crate::Result<std::fs::ReadDir> {
255
256        on_android!({
257            let path = match relative_path {
258                Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
259                None => self.resolve_path(base_dir)?,
260            };
261    
262            Ok(std::fs::read_dir(path)?)
263        })
264    }
265
266    /// Removes a file from the filesystem.  
267    /// 
268    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_file`].  
269    /// See [`PrivateStorage::resolve_path`] for details.  
270    /// 
271    /// # Support
272    /// All Android version.
273    pub fn remove_file(
274        &self,
275        base_dir: PrivateDir,
276        relative_path: impl AsRef<str>,
277    ) -> crate::Result<()> {
278
279        on_android!({
280            let path = self.resolve_path_with(base_dir, relative_path)?;
281            Ok(std::fs::remove_file(path)?)
282        })
283    }
284
285    /// Removes an empty directory.  
286    /// If you want to remove a directory that is not empty, as well as all of its contents recursively, consider using [`PrivateStorage::remove_dir_all`] instead.  
287    /// 
288    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir`].  
289    /// See [`PrivateStorage::resolve_path`] for details.  
290    /// 
291    /// # Support
292    /// All Android version.
293    pub fn remove_dir(
294        &self,
295        base_dir: PrivateDir,
296        relative_path: Option<&str>,
297    ) -> crate::Result<()> {
298
299        on_android!({
300            let path = match relative_path {
301                Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
302                None => self.resolve_path(base_dir)?,
303            };
304    
305            std::fs::remove_dir(path)?;
306            Ok(())
307        })
308    }
309
310    /// Removes a directory at this path, after removing all its contents. Use carefully!  
311    /// 
312    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir_all`].  
313    /// See [`PrivateStorage::resolve_path`] for details.  
314    /// 
315    /// # Support
316    /// All Android version.
317    pub fn remove_dir_all(
318        &self,
319        base_dir: PrivateDir,
320        relative_path: Option<&str>,
321    ) -> crate::Result<()> {
322
323        on_android!({
324            let path = match relative_path {
325                Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
326                None => self.resolve_path(base_dir)?,
327            };
328    
329            std::fs::remove_dir_all(path)?;
330            Ok(())
331        })
332    }
333
334    /// Returns Ok(true) if the path points at an existing entity.  
335    /// 
336    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::exists`].  
337    /// See [`PrivateStorage::resolve_path`] for details.  
338    /// 
339    /// # Support
340    /// All Android version.
341    pub fn exists(
342        &self,
343        base_dir: PrivateDir,
344        relative_path: impl AsRef<str>
345    ) -> crate::Result<bool> {
346
347        on_android!({
348            let path = self.resolve_path_with(base_dir, relative_path)?;
349            Ok(std::fs::exists(path)?)
350        })
351    }
352
353    /// Queries the file system to get information about a file, directory.  
354    /// 
355    /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::metadata`].  
356    /// See [`PrivateStorage::resolve_path`] for details.  
357    /// 
358    /// # Support
359    /// All Android version.
360    pub fn metadata(
361        &self,
362        base_dir: PrivateDir,
363        relative_path: Option<&str>,
364    ) -> crate::Result<std::fs::Metadata> {
365
366        on_android!({
367            let path = match relative_path {
368                Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
369                None => self.resolve_path(base_dir)?,
370            };
371    
372            Ok(std::fs::metadata(path)?)
373        })
374    }
375
376
377    #[allow(unused)]
378    pub(crate) fn create_new_tmp_file(&self) -> crate::Result<(std::fs::File, std::path::PathBuf)> {
379        on_android!({
380            let tmp_file_path = {
381                use std::sync::atomic::{AtomicUsize, Ordering};
382
383                static COUNTER: AtomicUsize = AtomicUsize::new(0);
384                let id = COUNTER.fetch_add(1, Ordering::Relaxed);
385
386                let tmp_dir_path = self.resolve_tmp_dir()?;
387                let _ = std::fs::create_dir_all(&tmp_dir_path);
388            
389                tmp_dir_path.join(format!("tmp file {id}"))
390            };
391            
392            let tmp_file = std::fs::File::create_new(&tmp_file_path)?;
393
394            Ok((tmp_file, tmp_file_path))
395        })
396    }
397
398    #[allow(unused)]
399    pub(crate) fn remove_all_tmp_files(&self) -> crate::Result<()> {
400        on_android!({
401            match std::fs::remove_dir_all(self.resolve_tmp_dir()?) {
402                Ok(_) => Ok(()),
403                Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
404                Err(e) => Err(e.into()),
405            }
406        })
407    }
408
409    #[allow(unused)]
410    pub(crate) fn resolve_tmp_dir(&self) -> crate::Result<std::path::PathBuf> {
411        on_android!({
412            const TMP_DIR_RELATIVE_PATH: &str = "pluginAndroidFs-tmpDir-01K486FKQ2BZSBGFD34RFH9FWJ";
413
414            self.resolve_path_with(PrivateDir::Cache, TMP_DIR_RELATIVE_PATH)
415        })
416    }
417}