diff --git a/src/test/file_navigation.rs b/src/test/file_navigation.rs new file mode 100644 index 00000000..80b8e618 --- /dev/null +++ b/src/test/file_navigation.rs @@ -0,0 +1,104 @@ +use crate::*; + + +#[test] +fn test_file_navigation() +{ + test("main.asm", "", Err(())); + test("main.asm", ".", Err(())); + test("main.asm", "sibling.asm", Ok("sibling.asm")); + test("main.asm", "./sibling.asm", Ok("sibling.asm")); + test("main.asm", "folder/inner.asm", Ok("folder/inner.asm")); + test("main.asm", "./folder/inner.asm", Ok("folder/inner.asm")); + test("main.asm", "..", Err(())); + test("main.asm", "../outer.asm", Err(())); + + test("./main.asm", "", Err(())); + test("./main.asm", ".", Err(())); + test("./main.asm", "sibling.asm", Ok("./sibling.asm")); + test("./main.asm", "./sibling.asm", Ok("./sibling.asm")); + test("./main.asm", "folder/inner.asm", Ok("./folder/inner.asm")); + test("./main.asm", "./folder/inner.asm", Ok("./folder/inner.asm")); + test("./main.asm", "..", Err(())); + test("./main.asm", "../outer.asm", Ok("outer.asm")); + + test("/main.asm", "", Err(())); + test("/main.asm", ".", Err(())); + test("/main.asm", "sibling.asm", Ok("/sibling.asm")); + test("/main.asm", "./sibling.asm", Ok("/sibling.asm")); + test("/main.asm", "folder/inner.asm", Ok("/folder/inner.asm")); + test("/main.asm", "./folder/inner.asm", Ok("/folder/inner.asm")); + test("/main.asm", "..", Err(())); + test("/main.asm", "../outer.asm", Ok("outer.asm")); + + test("C:/main.asm", "", Err(())); + test("C:/main.asm", ".", Err(())); + test("C:/main.asm", "sibling.asm", Ok("C:/sibling.asm")); + test("C:/main.asm", "./sibling.asm", Ok("C:/sibling.asm")); + test("C:/main.asm", "folder/inner.asm", Ok("C:/folder/inner.asm")); + test("C:/main.asm", "./folder/inner.asm", Ok("C:/folder/inner.asm")); + test("C:/main.asm", "..", Err(())); + test("C:/main.asm", "../outer.asm", Ok("outer.asm")); + + test("folder/inner.asm", "", Err(())); + test("folder/inner.asm", ".", Err(())); + test("folder/inner.asm", "sibling.asm", Ok("folder/sibling.asm")); + test("folder/inner.asm", "./sibling.asm", Ok("folder/sibling.asm")); + test("folder/inner.asm", "folder/inner.asm", Ok("folder/folder/inner.asm")); + test("folder/inner.asm", "./folder/inner.asm", Ok("folder/folder/inner.asm")); + test("folder/inner.asm", "..", Err(())); + test("folder/inner.asm", "../outer.asm", Ok("outer.asm")); + + test("./folder/inner.asm", "", Err(())); + test("./folder/inner.asm", ".", Err(())); + test("./folder/inner.asm", "sibling.asm", Ok("./folder/sibling.asm")); + test("./folder/inner.asm", "./sibling.asm", Ok("./folder/sibling.asm")); + test("./folder/inner.asm", "folder/inner.asm", Ok("./folder/folder/inner.asm")); + test("./folder/inner.asm", "./folder/inner.asm", Ok("./folder/folder/inner.asm")); + test("./folder/inner.asm", "..", Err(())); + test("./folder/inner.asm", "../outer.asm", Ok("./outer.asm")); + test("./folder/inner.asm", "../../outer.asm", Ok("outer.asm")); + + test("/folder/inner.asm", "", Err(())); + test("/folder/inner.asm", ".", Err(())); + test("/folder/inner.asm", "sibling.asm", Ok("/folder/sibling.asm")); + test("/folder/inner.asm", "./sibling.asm", Ok("/folder/sibling.asm")); + test("/folder/inner.asm", "folder/inner.asm", Ok("/folder/folder/inner.asm")); + test("/folder/inner.asm", "./folder/inner.asm", Ok("/folder/folder/inner.asm")); + test("/folder/inner.asm", "..", Err(())); + test("/folder/inner.asm", "../outer.asm", Ok("/outer.asm")); + test("/folder/inner.asm", "../../outer.asm", Ok("outer.asm")); + + test("C:/folder/inner.asm", "", Err(())); + test("C:/folder/inner.asm", ".", Err(())); + test("C:/folder/inner.asm", "sibling.asm", Ok("C:/folder/sibling.asm")); + test("C:/folder/inner.asm", "./sibling.asm", Ok("C:/folder/sibling.asm")); + test("C:/folder/inner.asm", "folder/inner.asm", Ok("C:/folder/folder/inner.asm")); + test("C:/folder/inner.asm", "./folder/inner.asm", Ok("C:/folder/folder/inner.asm")); + test("C:/folder/inner.asm", "..", Ok("C:")); + test("C:/folder/inner.asm", "../outer.asm", Ok("C:/outer.asm")); + test("C:/folder/inner.asm", "../../outer.asm", Ok("outer.asm")); + + test("folder/subfolder/inner.asm", "", Err(())); + test("folder/subfolder/inner.asm", ".", Err(())); + test("folder/subfolder/inner.asm", "sibling.asm", Ok("folder/subfolder/sibling.asm")); + test("folder/subfolder/inner.asm", "./sibling.asm", Ok("folder/subfolder/sibling.asm")); + test("folder/subfolder/inner.asm", "folder/inner.asm", Ok("folder/subfolder/folder/inner.asm")); + test("folder/subfolder/inner.asm", "./folder/inner.asm", Ok("folder/subfolder/folder/inner.asm")); + test("folder/subfolder/inner.asm", "..", Ok("folder")); + test("folder/subfolder/inner.asm", "../outer.asm", Ok("folder/outer.asm")); + test("folder/subfolder/inner.asm", "../../outer.asm", Ok("outer.asm")); + test("folder/subfolder/inner.asm", "../folder/outer.asm", Ok("folder/folder/outer.asm")); +} + + +fn test(base: &str, relative: &str, expected: Result<&str, ()>) +{ + let mut report = diagn::Report::new(); + let span = diagn::Span::new_dummy(); + + let result = util::filename_navigate( + &mut report, span, base, relative); + + assert_eq!(result.as_deref(), expected.as_deref()); +} \ No newline at end of file diff --git a/src/test/mod.rs b/src/test/mod.rs index 45856492..40053988 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -5,6 +5,7 @@ mod examples; mod excerpt; mod expr; mod file; +mod file_navigation; mod lib; diff --git a/src/util/filename.rs b/src/util/file_navigation.rs similarity index 72% rename from src/util/filename.rs rename to src/util/file_navigation.rs index e7360a7e..5310c637 100644 --- a/src/util/filename.rs +++ b/src/util/file_navigation.rs @@ -12,7 +12,7 @@ pub fn is_std_path( } -pub fn filename_validate( +pub fn filename_validate_relative( report: &mut diagn::Report, span: diagn::Span, filename: &str) @@ -48,18 +48,18 @@ pub fn filename_navigate( report: &mut diagn::Report, span: diagn::Span, current: &str, - nav: &str) + relative: &str) -> Result { - if is_std_path(nav) + if is_std_path(relative) { - return Ok(nav.to_string()); + return Ok(relative.to_string()); } let current = current.replace("\\", "/"); - let nav = nav.replace("\\", "/"); + let nav = relative.replace("\\", "/"); - filename_validate( + filename_validate_relative( report, span, &nav)?; @@ -79,15 +79,26 @@ pub fn filename_navigate( path_components.clear(); } - // Add the new path components - for split in nav.split("/") + // Add the new path components, collapsing `.` and empty components + let relative_components: Vec<&str> = nav + .split("/") + .filter(|s| s.len() > 0 && s != &".") + .collect(); + + if relative_components.len() == 0 + { + report.error_span( + "invalid filename", + span); + + return Err(()); + } + + for split in relative_components { path_components.push(split); } - // Collapse `.` and empty components - path_components.retain(|s| s.len() > 0 && s != &"."); - // Collapse `..` components let mut new_path_components = Vec::new(); for i in 0..path_components.len() @@ -119,5 +130,17 @@ pub fn filename_navigate( } } + if new_path_components.len() == 0 || + new_filename == "" || + new_filename == "." || + new_filename == "/" + { + report.error_span( + "invalid filename", + span); + + return Err(()); + } + Ok(new_filename) } \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index f15cc26d..63fb589d 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -24,11 +24,11 @@ pub use self::fileserver::{ FILESERVER_MOCK_WRITE_FILENAME_SUFFIX, }; -mod filename; -pub use self::filename::{ +mod file_navigation; +pub use self::file_navigation::{ STD_PATH_PREFIX, is_std_path, - filename_validate, + filename_validate_relative, filename_navigate, };