tauri_store/collection/
path.rs

1use super::StoreCollection;
2use crate::error::Result;
3use crate::io_err;
4use crate::meta::Meta;
5use crate::store::{append_filename, Store, StoreResource};
6use itertools::Itertools;
7use std::path::{Path, PathBuf};
8use std::{fs, mem};
9use tauri::Runtime;
10
11impl<R: Runtime> StoreCollection<R> {
12  /// Directory where the stores are saved.
13  pub fn path(&self) -> PathBuf {
14    self.path.lock().unwrap().clone()
15  }
16
17  /// Sets the directory where the stores are saved.
18  /// This will move all *currently active* stores to the new directory.
19  pub fn set_path(&self, path: impl AsRef<Path>) -> Result<()> {
20    let new = path.as_ref().to_path_buf();
21    if new == *self.path.lock().unwrap() {
22      return Ok(());
23    }
24
25    fs::create_dir_all(&new)?;
26
27    let resources = self
28      .rids()
29      .into_iter()
30      .map(|rid| StoreResource::get(&self.app, rid))
31      .process_results(|res| res.collect_vec())
32      .unwrap_or_default();
33
34    if resources.is_empty() {
35      *self.path.lock().unwrap() = new;
36      return Ok(());
37    }
38
39    // Locking all the stores first ensures that none of them will attempt to lock the path.
40    // By itself, this should not cause a deadlock, as the stores don't depend on each other.
41    {
42      let stores = resources
43        .iter()
44        .map(|resource| resource.inner.lock())
45        .process_results(|res| res.collect_vec())
46        .unwrap_or_default();
47
48      let mut lock = self.path.lock().unwrap();
49      let from = mem::replace(&mut *lock, new);
50      let to = &*lock;
51
52      for store in stores {
53        move_store(&*store, &from, to)?;
54      }
55    }
56
57    // We need to ensure that the path lock is released before calling this.
58    Meta::write(self)?;
59
60    Ok(())
61  }
62}
63
64fn move_store<R>(store: &Store<R>, from: &Path, to: &Path) -> Result<()>
65where
66  R: Runtime,
67{
68  // Calling `Store::path` would be a deadlock!
69  // We need to manually append the filename to the path.
70  let current = append_filename(from, &store.id);
71  let new = append_filename(to, &store.id);
72
73  if new.try_exists()? {
74    let path = new.display();
75    return io_err!(AlreadyExists, "file already exists: {path}");
76  }
77
78  fs::copy(&current, &new)?;
79  fs::remove_file(&current)?;
80
81  Ok(())
82}