diff options
| -rw-r--r-- | src/lib.rs | 86 |
1 files changed, 73 insertions, 13 deletions
@@ -1,17 +1,42 @@ -use std::path::{Component, Path, PathBuf}; +use std::{env, path::{Component, Path, PathBuf}}; #[derive(Debug)] pub enum NormalizeError { EmptyInput, + CwdNotAbsolute, } pub fn normalize_path(root: &Path, origin: &Path, input: &Path) -> Result<PathBuf, NormalizeError> { + let cwd = env::current_dir().map_err(|_| NormalizeError::CwdNotAbsolute)?; + normalize_path_with_preset_cwd(root, origin, input, &cwd) +} + +fn normalize_path_with_preset_cwd( + root: &Path, + origin: &Path, + input: &Path, + cwd: &Path, +) -> Result<PathBuf, NormalizeError> { if input.as_os_str().is_empty() { return Err(NormalizeError::EmptyInput); } + if !cwd.is_absolute() { + return Err(NormalizeError::CwdNotAbsolute); + } + let input = if input.is_absolute() { + input.to_path_buf() + } else if origin.is_absolute() { + origin.join(input) + } else { + cwd.join(origin).join(input) + }; + let root = if root.is_absolute() { + root.to_path_buf() + } else { + cwd.join(root) + }; let mut stack = Vec::new(); - let origin_joint_input = origin.join(input); - for component in origin_joint_input.components() { + for component in input.components() { match component { Component::CurDir => (), Component::ParentDir => { @@ -22,11 +47,11 @@ pub fn normalize_path(root: &Path, origin: &Path, input: &Path) -> Result<PathBu Component::RootDir => stack.push(component), } } - let normalized_origin_join_input = PathBuf::from_iter(stack); - Ok(normalize_to_root(&normalized_origin_join_input, root)) + let normalized_input = PathBuf::from_iter(stack); + Ok(normalize_to_root(normalized_input, &root)) } -fn normalize_to_root(target: &Path, mut root: &Path) -> PathBuf { +fn normalize_to_root(target: PathBuf, mut root: &Path) -> PathBuf { let mut prefix = PathBuf::new(); loop { if let Ok(suffix) = target.strip_prefix(root) { @@ -36,7 +61,7 @@ fn normalize_to_root(target: &Path, mut root: &Path) -> PathBuf { prefix.push(".."); root = new_root; } else { - return target.to_path_buf(); + return target; } } } @@ -45,52 +70,87 @@ fn normalize_to_root(target: &Path, mut root: &Path) -> PathBuf { mod tests { use std::path::Path; - use super::{NormalizeError, normalize_path}; + use super::{NormalizeError, normalize_path_with_preset_cwd}; #[test] fn empty_path_returns_error() { + let fake_cwd = Path::new("/sandbox"); let root = Path::new(""); let origin_dir = Path::new(""); let input = Path::new(""); - let result = normalize_path(root, origin_dir, input); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); assert!(matches!(result, Err(NormalizeError::EmptyInput))); } #[test] fn plain_filename_with_root_at_cwd_returns_filename() { + let fake_cwd = Path::new("/sandbox"); let root = Path::new(""); let origin_dir = Path::new(""); let input = Path::new("main.rs"); - let result = normalize_path(root, origin_dir, input); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); assert!(result.is_ok()); assert_eq!(result.unwrap(), Path::new("main.rs")); } #[test] fn relative_path_from_origin_is_resolved() { + let fake_cwd = Path::new("/sandbox"); let root = Path::new(""); let origin_dir = Path::new("src"); let input = Path::new("../main.rs"); - let result = normalize_path(root, origin_dir, input); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); assert!(result.is_ok()); assert_eq!(result.unwrap(), Path::new("main.rs")); } #[test] fn path_is_made_relative_to_root() { + let fake_cwd = Path::new("/sandbox"); let root = Path::new("/project"); let origin_dir = Path::new("/project/src"); let input = Path::new("main.rs"); - let result = normalize_path(root, origin_dir, input); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); assert_eq!(result.unwrap(), Path::new("src/main.rs")); } #[test] fn path_is_made_relative_to_root_from_outside() { + let fake_cwd = Path::new("/sandbox"); let root = Path::new("/project"); let origin_dir = Path::new("/outside"); let input = Path::new("main.rs"); - let result = normalize_path(root, origin_dir, input); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); + assert_eq!(result.unwrap(), Path::new("../outside/main.rs")); + } + + #[test] + fn path_is_made_relative_to_root_even_if_root_dir_is_relative() { + let fake_cwd = Path::new("/sandbox"); + let root = Path::new("project"); + let origin_dir = Path::new("/sandbox/outside"); + let input = Path::new("main.rs"); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); + assert_eq!(result.unwrap(), Path::new("../outside/main.rs")); + } + + #[test] + fn absolute_inputs_work() { + let fake_cwd = Path::new("/sandbox"); + let root = Path::new("project"); + let origin_dir = Path::new("/sandbox/outside"); + let input = Path::new("/sandbox/main.rs"); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); + assert_eq!(result.unwrap(), Path::new("../main.rs")); + } + + #[test] + fn path_is_made_relative_to_root_even_if_origin_dir_is_relative() { + let fake_cwd = Path::new("/sandbox"); + let root = Path::new("/sandbox/project"); + let origin_dir = Path::new("outside"); + let input = Path::new("main.rs"); + let result = normalize_path_with_preset_cwd(root, origin_dir, input, fake_cwd); assert_eq!(result.unwrap(), Path::new("../outside/main.rs")); } } |
