diff --git a/src/common_paths_tests.rs b/src/common_paths_tests.rs new file mode 100644 index 0000000..f38fd82 --- /dev/null +++ b/src/common_paths_tests.rs @@ -0,0 +1,29 @@ +use std::path::PathBuf; + +#[test] +fn empty() { + assert_eq!(crate::common_parent::(&[]), PathBuf::new()); +} + +#[test] +fn single1() { + assert_eq!(crate::common_parent::<&str>(&["a"]).as_path(), ""); +} + +#[test] +fn single2() { + assert_eq!(crate::common_parent::<&str>(&["/a"]).as_path(), "/"); +} + +#[test] +fn double() { + assert_eq!(crate::common_parent::<&str>(&["d/a", "d/b"]).as_path(), "d"); +} + +#[test] +fn dir_file() { + assert_eq!( + crate::common_parent::<&str>(&["./d", "./d/f"]).as_path(), + "." + ); +} diff --git a/src/lib.rs b/src/lib.rs index b6d6f8f..6138802 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +use std::path::Path; use std::{ffi::OsString, io::Read, path::PathBuf, str::FromStr, string::FromUtf8Error}; use anyhow::bail; @@ -80,6 +81,27 @@ pub fn actions_token() -> Result { Ok(SecretString::new(str.into_boxed_str())) } +pub fn common_parent>(paths: &[P]) -> PathBuf { + let Some((path0, paths)) = paths.split_first() else { + return PathBuf::new(); + }; + let mut common = PathBuf::new(); + for comp in path0.as_ref() { + common.push(comp); + if !paths.iter().all(|p| p.as_ref().starts_with(&common)) { + break; + } + } + // first path is common to all the paths, or the last component does not match all the paths + // + // either way, we need to pop a single component from the end + common.pop(); + common +} + +#[cfg(test)] +mod common_paths_tests; + #[derive(Debug)] pub struct Client { http_client: HttpClient,