Compare commits

...

2 commits

Author SHA1 Message Date
8b0dbb2314 fix: add lifetime annotation (clippy) 2024-12-01 19:19:28 +09:00
772689ab6a change: change type of relative path shared on multiple platforms to Vector<String>
Parsers for path on Windows and Unix are different on separator character treatment.
Replacing to Vector<String> avoids this differenct for cross-platform compatibility.
2024-12-01 19:14:57 +09:00
4 changed files with 43 additions and 31 deletions

View file

@ -33,22 +33,27 @@ pub struct BackupTarget {
/// Use `String` for serialization/deserialization. /// Use `String` for serialization/deserialization.
pub storage: String, pub storage: String,
/// Relative path to the `storage`. /// Relative path to the `storage`.
pub path: PathBuf, pub path: Vec<String>,
} }
impl BackupTarget { impl BackupTarget {
pub fn new(storage_name: String, relative_path: PathBuf) -> Self { pub fn new(storage_name: String, relative_path: PathBuf) -> Result<Self> {
BackupTarget { let relative_path = relative_path
.components()
.map(|c| c.as_os_str().to_str().map(|s| s.to_owned()))
.collect::<Option<_>>()
.context("Path contains non-utf8 character")?;
Ok(BackupTarget {
storage: storage_name, storage: storage_name,
path: relative_path, path: relative_path,
} })
} }
/// Get full path of the [`BackupTarget`]. /// Get full path of the [`BackupTarget`].
pub fn path(&self, storages: &Storages, device: &Device) -> Option<PathBuf> { pub fn path(&self, storages: &Storages, device: &Device) -> Option<PathBuf> {
let parent = storages.get(&self.storage).unwrap(); let parent = storages.get(&self.storage).unwrap();
let parent_path = parent.mount_path(device)?; let parent_path = parent.mount_path(device)?;
Some(parent_path.join(self.path.clone())) Some(parent_path.join(self.path.clone().iter().collect::<PathBuf>()))
} }
} }
@ -175,7 +180,7 @@ impl Backup {
&self.name &self.name
} }
pub fn device<'a>(&'a self, devices: &'a [Device]) -> Option<&Device> { pub fn device<'a>(&'a self, devices: &'a [Device]) -> Option<&'a Device> {
devices.iter().find(|dev| dev.name() == self.device) devices.iter().find(|dev| dev.name() == self.device)
} }

View file

@ -89,8 +89,8 @@ fn new_backup(
Ok(Backup::new( Ok(Backup::new(
name, name,
device.name(), device.name(),
src_target, src_target?,
dest_target, dest_target?,
command, command,
)) ))
} }
@ -361,9 +361,9 @@ mod test {
&storages, &storages,
)?; )?;
assert!(backup.source().storage == "online"); assert!(backup.source().storage == "online");
assert_eq!(backup.source().path, PathBuf::from("docs")); assert_eq!(backup.source().path, vec!["docs"]);
assert!(backup.destination().storage == "online"); assert!(backup.destination().storage == "online");
assert!(backup.destination().path == PathBuf::from("tmp")); assert!(backup.destination().path == vec!["tmp"]);
Ok(()) Ok(())
} }
} }

View file

@ -146,7 +146,7 @@ fn parent_backups<'a>(
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::path::PathBuf; use std::{path::PathBuf, vec};
use crate::{ use crate::{
backups::{self, ExternallyInvoked}, backups::{self, ExternallyInvoked},
@ -201,11 +201,11 @@ mod test {
device1.name().to_string(), device1.name().to_string(),
backups::BackupTarget { backups::BackupTarget {
storage: "storage_1".to_string(), storage: "storage_1".to_string(),
path: PathBuf::from("bar"), path: vec!["bar".to_string()],
}, },
backups::BackupTarget { backups::BackupTarget {
storage: "storage_1".to_string(), storage: "storage_1".to_string(),
path: PathBuf::from("hoge"), path: vec!["hoge".to_string()],
}, },
backups::BackupCommand::ExternallyInvoked(ExternallyInvoked::new( backups::BackupCommand::ExternallyInvoked(ExternallyInvoked::new(
"cmd".to_string(), "cmd".to_string(),
@ -217,11 +217,11 @@ mod test {
device2.name().to_string(), device2.name().to_string(),
backups::BackupTarget { backups::BackupTarget {
storage: "storage_1".to_string(), storage: "storage_1".to_string(),
path: PathBuf::from(""), path: vec!["".to_string()],
}, },
backups::BackupTarget { backups::BackupTarget {
storage: "storage_3".to_string(), storage: "storage_3".to_string(),
path: PathBuf::from("foo"), path: vec!["foo".to_string()],
}, },
backups::BackupCommand::ExternallyInvoked(ExternallyInvoked::new( backups::BackupCommand::ExternallyInvoked(ExternallyInvoked::new(
"cmd".to_string(), "cmd".to_string(),

View file

@ -2,6 +2,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::{collections::BTreeMap, fmt, path}; use std::{collections::BTreeMap, fmt, path};
use crate::devices; use crate::devices;
@ -17,7 +18,7 @@ pub struct Directory {
/// ID of parent storage. /// ID of parent storage.
parent: String, parent: String,
/// Relative path to the parent storage. /// Relative path to the parent storage.
relative_path: path::PathBuf, relative_path: Vec<String>,
pub notes: String, pub notes: String,
/// [`devices::Device`] name and localinfo pairs. /// [`devices::Device`] name and localinfo pairs.
local_infos: BTreeMap<String, LocalInfo>, local_infos: BTreeMap<String, LocalInfo>,
@ -34,14 +35,19 @@ impl Directory {
relative_path: path::PathBuf, relative_path: path::PathBuf,
notes: String, notes: String,
local_infos: BTreeMap<String, LocalInfo>, local_infos: BTreeMap<String, LocalInfo>,
) -> Directory { ) -> Result<Directory> {
Directory { let relative_path = relative_path
.components()
.map(|c| c.as_os_str().to_str().map(|s| s.to_owned()))
.collect::<Option<Vec<_>>>()
.context("Path contains non-utf8 character")?;
Ok(Directory {
name, name,
parent, parent,
relative_path, relative_path,
notes, notes,
local_infos, local_infos,
} })
} }
pub fn try_from_device_path( pub fn try_from_device_path(
@ -56,23 +62,23 @@ impl Directory {
.context("Failed to compare diff of paths")?; .context("Failed to compare diff of paths")?;
trace!("Selected parent: {}", parent.name()); trace!("Selected parent: {}", parent.name());
let local_info = LocalInfo::new(alias, path); let local_info = LocalInfo::new(alias, path);
Ok(Directory::new( Directory::new(
name, name,
parent.name().to_string(), parent.name().to_string(),
diff_path, diff_path,
notes, notes,
BTreeMap::from([(device.name(), local_info)]), BTreeMap::from([(device.name(), local_info)]),
)) )
} }
pub fn update_note(self, notes: String) -> Directory { pub fn update_note(self, notes: String) -> Directory {
Directory::new( Directory {
self.name, name: self.name,
self.parent, parent: self.parent,
self.relative_path, relative_path: self.relative_path,
notes, notes,
self.local_infos, local_infos: self.local_infos,
) }
} }
/// Resolve mount path of directory with current device. /// Resolve mount path of directory with current device.
@ -82,7 +88,7 @@ impl Directory {
.context("Can't find parent storage")? .context("Can't find parent storage")?
.mount_path(device) .mount_path(device)
.context("Can't find mount path")?; .context("Can't find mount path")?;
Ok(parent_mount_path.join(self.relative_path.clone())) Ok(parent_mount_path.join(self.relative_path.clone().iter().collect::<PathBuf>()))
} }
} }
@ -121,7 +127,7 @@ impl StorageExt for Directory {
} }
// Get parent `&Storage` of directory. // Get parent `&Storage` of directory.
fn parent<'a>(&'a self, storages: &'a Storages) -> Option<&Storage> { fn parent<'a>(&'a self, storages: &'a Storages) -> Option<&'a Storage> {
storages.get(&self.parent) storages.get(&self.parent)
} }
} }
@ -133,7 +139,7 @@ impl fmt::Display for Directory {
"S {name:<10} < {parent:<10}{relative_path:<10} : {notes}", "S {name:<10} < {parent:<10}{relative_path:<10} : {notes}",
name = self.name(), name = self.name(),
parent = self.parent, parent = self.parent,
relative_path = self.relative_path.display(), relative_path = self.relative_path.iter().collect::<PathBuf>().display(),
notes = self.notes, notes = self.notes,
) )
} }
@ -177,7 +183,8 @@ mod test {
"subdir".into(), "subdir".into(),
"some note".to_string(), "some note".to_string(),
local_infos, local_infos,
); )
.unwrap();
let mut storages = Storages::new(); let mut storages = Storages::new();
storages.add(storages::Storage::Physical(physical)).unwrap(); storages.add(storages::Storage::Physical(physical)).unwrap();
storages.add(Storage::SubDirectory(directory)).unwrap(); storages.add(Storage::SubDirectory(directory)).unwrap();