Skip to content

Commit

Permalink
fix #215: improve relative path navigation algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed Feb 1, 2025
1 parent 2c774b8 commit d524b97
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 14 deletions.
104 changes: 104 additions & 0 deletions src/test/file_navigation.rs
Original file line number Diff line number Diff line change
@@ -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());
}
1 change: 1 addition & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod examples;
mod excerpt;
mod expr;
mod file;
mod file_navigation;
mod lib;


Expand Down
45 changes: 34 additions & 11 deletions src/util/filename.rs → src/util/file_navigation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -48,18 +48,18 @@ pub fn filename_navigate(
report: &mut diagn::Report,
span: diagn::Span,
current: &str,
nav: &str)
relative: &str)
-> Result<String, ()>
{
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)?;
Expand All @@ -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()
Expand Down Expand Up @@ -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)
}
6 changes: 3 additions & 3 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down

0 comments on commit d524b97

Please sign in to comment.