149 lines
3.5 KiB
Rust
149 lines
3.5 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
ops::Deref,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use regex::Regex;
|
|
|
|
use crate::app::Error;
|
|
|
|
use super::storage;
|
|
use super::Config;
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct MountDir {
|
|
pub source: PathBuf,
|
|
pub name: String,
|
|
}
|
|
|
|
impl TryFrom<storage::MountDir> for MountDir {
|
|
type Error = Error;
|
|
|
|
fn try_from(mount_dir: storage::MountDir) -> Result<Self, Self::Error> {
|
|
// TODO validation
|
|
Ok(Self {
|
|
source: sanitize_path(&mount_dir.source),
|
|
name: mount_dir.name,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<MountDir> for storage::MountDir {
|
|
fn from(m: MountDir) -> Self {
|
|
Self {
|
|
source: m.source,
|
|
name: m.name,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Config {
|
|
pub fn set_mounts(&mut self, mount_dirs: Vec<storage::MountDir>) -> Result<(), Error> {
|
|
let mut new_mount_dirs = HashMap::new();
|
|
for mount_dir in mount_dirs {
|
|
let mount_dir = <storage::MountDir as TryInto<MountDir>>::try_into(mount_dir)?;
|
|
new_mount_dirs.insert(mount_dir.name.clone(), mount_dir);
|
|
}
|
|
self.mount_dirs = new_mount_dirs;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn resolve_virtual_path<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf, Error> {
|
|
for (name, mount) in &self.mount_dirs {
|
|
if let Ok(p) = virtual_path.as_ref().strip_prefix(name) {
|
|
return if p.components().count() == 0 {
|
|
Ok(mount.source.clone())
|
|
} else {
|
|
Ok(mount.source.join(p))
|
|
};
|
|
}
|
|
}
|
|
Err(Error::CouldNotMapToRealPath(virtual_path.as_ref().into()))
|
|
}
|
|
}
|
|
|
|
fn sanitize_path(source: &PathBuf) -> PathBuf {
|
|
let path_string = source.to_string_lossy();
|
|
let separator_regex = Regex::new(r"\\|/").unwrap();
|
|
let mut correct_separator = String::new();
|
|
correct_separator.push(std::path::MAIN_SEPARATOR);
|
|
let path_string = separator_regex.replace_all(&path_string, correct_separator.as_str());
|
|
PathBuf::from(path_string.deref())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn can_resolve_virtual_paths() {
|
|
let raw_config = storage::Config {
|
|
mount_dirs: vec![storage::MountDir {
|
|
name: "root".to_owned(),
|
|
source: PathBuf::from("test_dir"),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
|
|
let config: Config = raw_config.try_into().unwrap();
|
|
|
|
let test_cases = vec![
|
|
(vec!["root"], vec!["test_dir"]),
|
|
(
|
|
vec!["root", "somewhere", "something.png"],
|
|
vec!["test_dir", "somewhere", "something.png"],
|
|
),
|
|
];
|
|
|
|
for (r#virtual, real) in test_cases {
|
|
let real_path: PathBuf = real.iter().collect();
|
|
let virtual_path: PathBuf = r#virtual.iter().collect();
|
|
let converted_path = config.resolve_virtual_path(&virtual_path).unwrap();
|
|
assert_eq!(converted_path, real_path);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sanitizes_paths() {
|
|
let mut correct_path = PathBuf::new();
|
|
if cfg!(target_os = "windows") {
|
|
correct_path.push("C:\\");
|
|
} else {
|
|
correct_path.push("/usr");
|
|
}
|
|
correct_path.push("some");
|
|
correct_path.push("path");
|
|
|
|
let tests = if cfg!(target_os = "windows") {
|
|
vec![
|
|
r#"C:/some/path"#,
|
|
r#"C:\some\path"#,
|
|
r#"C:\some\path\"#,
|
|
r#"C:\some\path\\\\"#,
|
|
r#"C:\some/path//"#,
|
|
]
|
|
} else {
|
|
vec![
|
|
r#"/usr/some/path"#,
|
|
r#"/usr\some\path"#,
|
|
r#"/usr\some\path\"#,
|
|
r#"/usr\some\path\\\\"#,
|
|
r#"/usr\some/path//"#,
|
|
]
|
|
};
|
|
|
|
for test in tests {
|
|
let raw_config = storage::Config {
|
|
mount_dirs: vec![storage::MountDir {
|
|
name: "root".to_owned(),
|
|
source: PathBuf::from(test),
|
|
}],
|
|
..Default::default()
|
|
};
|
|
let config: Config = raw_config.try_into().unwrap();
|
|
let converted_path = config.resolve_virtual_path(&PathBuf::from("root")).unwrap();
|
|
assert_eq!(converted_path, correct_path);
|
|
}
|
|
}
|
|
}
|