use std::path::{Path, PathBuf};
use crate::{error::SourceSubPathNotUnderBaseSourceDirectory, file::CollidingFileBehaviour};
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum CollidingSubDirectoryBehaviour {
Abort,
Continue,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DestinationDirectoryRule {
DisallowExisting,
AllowEmpty,
AllowNonEmpty {
colliding_file_behaviour: CollidingFileBehaviour,
colliding_subdirectory_behaviour: CollidingSubDirectoryBehaviour,
},
}
impl Default for DestinationDirectoryRule {
fn default() -> Self {
Self::AllowEmpty
}
}
impl DestinationDirectoryRule {
pub(crate) fn allows_overwriting_existing_destination_files(&self) -> bool {
matches!(
self,
Self::AllowNonEmpty {
colliding_file_behaviour: CollidingFileBehaviour::Overwrite,
..
}
)
}
pub(crate) fn allows_existing_destination_subdirectories(&self) -> bool {
matches!(
self,
Self::AllowNonEmpty {
colliding_subdirectory_behaviour: CollidingSubDirectoryBehaviour::Continue,
..
}
)
}
}
pub(crate) fn join_relative_source_path_onto_destination(
source_base_directory_path: &Path,
source_sub_path: &Path,
target_base_directory_path: &Path,
) -> Result<PathBuf, SourceSubPathNotUnderBaseSourceDirectory> {
if source_base_directory_path.eq(source_sub_path) {
return Ok(target_base_directory_path.to_path_buf());
}
let source_sub_path_relative_to_base = source_sub_path
.strip_prefix(source_base_directory_path)
.map_err(|_| SourceSubPathNotUnderBaseSourceDirectory {
path: source_base_directory_path.join(source_sub_path),
})?;
Ok(target_base_directory_path.join(source_sub_path_relative_to_base))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn properly_rejoin_source_subpath_onto_target() {
let root_a = Path::new("/hello/there");
let foo = Path::new("/hello/there/some/content");
let root_b = Path::new("/different/root");
assert_eq!(
join_relative_source_path_onto_destination(root_a, foo, root_b).unwrap(),
Path::new("/different/root/some/content"),
"rejoin_source_subpath_onto_target did not rejoin the path properly."
);
let foo = Path::new("/foo");
let foo_hello_world = Path::new("/foo/abc/hello-world.txt");
let bar = Path::new("/bar");
assert_eq!(
join_relative_source_path_onto_destination(foo, foo_hello_world, bar).unwrap(),
Path::new("/bar/abc/hello-world.txt")
);
}
#[test]
fn error_on_subpath_not_being_under_source_root() {
let root_a = Path::new("/hello/there");
let foo = Path::new("/completely/different/path");
let root_b = Path::new("/different/root");
let rejoin_result = join_relative_source_path_onto_destination(root_a, foo, root_b);
assert!(
rejoin_result.is_err(),
"rejoin_source_subpath_onto_target did not return Err when \
the source path to rejoin wasn't under the source root path"
);
}
}