use std::path::PathBuf;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct OutputFolderKey(pub String);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct OutputFolder(pub OutputFolderKey, pub PathBuf);
impl std::fmt::Display for OutputFolderKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl OutputFolder {
const SEPARATOR: char = ':';
#[must_use]
pub fn is_any_parent_of_another(outputs: &[Self]) -> Option<(Self, Self)> {
for (i, output1) in outputs.iter().enumerate() {
for output2 in &outputs[i + 1..] {
#[expect(clippy::suspicious_operation_groupings, reason = "false positive")]
if output1.1 != output2.1
&& (output1.1.starts_with(&output2.1) || output2.1.starts_with(&output1.1))
{
return Some((output1.clone(), output2.clone()));
}
}
}
None
}
}
impl std::str::FromStr for OutputFolder {
type Err = anyhow::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
use anyhow::Context as _;
let (key, val) = value.split_once(Self::SEPARATOR).with_context(|| {
format!(
"separator '{}' not found in output folder string",
Self::SEPARATOR
)
})?;
Ok(Self(OutputFolderKey(key.to_string()), PathBuf::from(val)))
}
}
#[cfg(test)]
mod tests {
use test_log::test;
#[test]
fn parse_output_path() {
use super::{OutputFolder, OutputFolderKey};
assert_eq!(
"a".parse::<OutputFolder>().unwrap_err().to_string(),
"separator ':' not found in output folder string"
);
assert_eq!(
"a:D:\\b".parse::<OutputFolder>().unwrap(),
OutputFolder(OutputFolderKey("a".into()), "D:\\b".into())
);
}
#[test]
fn is_any_parent_of_another() {
use super::{OutputFolder, OutputFolderKey};
let p = |p: &str| OutputFolder(OutputFolderKey(String::new()), p.into());
let f = |pp: &[OutputFolder]| OutputFolder::is_any_parent_of_another(pp).is_some();
assert!(!f(&[]));
assert!(!f(&[p(""), p("")]));
assert!(f(&[p("a"), p("a/b")]));
assert!(f(&[p("a/b"), p("a")]));
assert!(!f(&[p("/a"), p("/b")]));
assert!(!f(&[p("/"), p("/")]));
assert!(f(&[p("/a"), p("/a/b")]));
assert!(f(&[p("/a/b"), p("/a")]));
assert!(!f(&[p("C:/a"), p("C:/b")]));
assert!(!f(&[p("C:/"), p("C:/")]));
assert!(f(&[p("C:/a"), p("C:/a/b")]));
assert!(f(&[p("C:/a"), p("C:/a/b")]));
}
}