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