tauri_store/collection/
path.rs

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