use std::collections::BTreeMap;
use crate::programs::NoResolver;
pub trait PathResolver {
fn resolve(&self, path: &str) -> Option<String>;
}
impl PathResolver for NoResolver {
fn resolve(&self, _path: &str) -> Option<String> {
None
}
}
#[derive(Debug, Default, Clone)]
pub struct MapPathResolver {
pub links: BTreeMap<String, String>,
}
impl PathResolver for MapPathResolver {
fn resolve(&self, path: &str) -> Option<String> {
self.links
.iter()
.filter_map(|(link, target)| {
let rest = path.strip_prefix(link.as_str())?;
(rest.is_empty() || rest.starts_with('/')).then_some((link.len(), target, rest))
})
.max_by_key(|(len, _, _)| *len)
.map(|(_, target, rest)| format!("{target}{rest}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn map_resolver_replaces_the_longest_linked_ancestor() {
let r = MapPathResolver {
links: BTreeMap::from([
("/p/s".to_owned(), "/h/.ssh".to_owned()),
("/p/s/k".to_owned(), "/h/.aws/credentials".to_owned()),
]),
};
assert_eq!(r.resolve("/p/s").as_deref(), Some("/h/.ssh"));
assert_eq!(r.resolve("/p/s/id_rsa").as_deref(), Some("/h/.ssh/id_rsa"));
assert_eq!(r.resolve("/p/s/k").as_deref(), Some("/h/.aws/credentials"));
assert_eq!(r.resolve("/p/sx/id_rsa"), None);
assert_eq!(r.resolve("/p/src/main.rs"), None);
assert_eq!(NoResolver.resolve("/p/s"), None);
}
}