use std::collections::BTreeMap;
use crate::model::Element;
use crate::path::ElementPath;
const URL_CHARACTERS: [char; 4] = [':', '/', '?', '#'];
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Link<'a> {
Element(ElementPath),
Url(&'a str),
}
impl<'a> Link<'a> {
pub fn resolve(raw: &'a str, elements: &BTreeMap<ElementPath, Element>) -> Self {
let Ok(path) = raw.parse::<ElementPath>() else {
return Self::Url(raw);
};
let is_element = elements.contains_key(&path);
if is_element {
return Self::Element(path);
}
Self::Url(raw)
}
pub fn path_shaped_url(&self) -> Option<ElementPath> {
let Self::Url(raw) = self else {
return None;
};
let is_url_shaped = Self::is_url_shaped(raw);
if is_url_shaped {
return None;
}
raw.parse().ok()
}
pub fn is_url_shaped(raw: &str) -> bool {
raw.contains(URL_CHARACTERS)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn backend_with_api() -> BTreeMap<ElementPath, Element> {
let backend = ElementPath::from_segments(["backend"]);
let api = ElementPath::from_segments(["backend", "api"]);
BTreeMap::from([
(backend, Element::new("backend")),
(api, Element::new("API")),
])
}
#[test]
fn a_link_naming_an_element_resolves_to_it_and_anything_else_stays_a_url() {
let elements = backend_with_api();
let api = ElementPath::from_segments(["backend", "api"]);
let cases = [
("backend.api", Link::Element(api)),
("backend.apii", Link::Url("backend.apii")),
("https://example.com", Link::Url("https://example.com")),
("./guide.html", Link::Url("./guide.html")),
("", Link::Url("")),
];
for (raw, expected_link) in cases {
let link = Link::resolve(raw, &elements);
assert_eq!(link, expected_link, "{raw:?}");
}
}
#[test]
fn only_urls_without_url_characters_are_shaped_like_an_element_path() {
let elements = backend_with_api();
let typo = ElementPath::from_segments(["backend", "apii"]);
let cases = [
("backend.apii", Some(typo)),
("backend.api", None),
("docs/guide.html", None),
("mailto:team@example.com", None),
("#section", None),
("..", None),
];
for (raw, expected_path) in cases {
let link = Link::resolve(raw, &elements);
let path_shaped = link.path_shaped_url();
assert_eq!(path_shaped, expected_path, "{raw:?}");
}
}
}