use std::collections::HashSet;
pub struct ContentGraph<'a> {
pub parts: &'a [String],
pub references: &'a [(String, Vec<String>)],
}
#[derive(Debug, PartialEq, Eq)]
pub enum ContentViolation {
BrokenReference { from: String, to: String },
}
pub fn validate_content_references(graph: &ContentGraph) -> Vec<ContentViolation> {
let parts: HashSet<&str> = graph.parts.iter().map(String::as_str).collect();
let mut violations = Vec::new();
for (from, targets) in graph.references {
for target in targets {
if !parts.contains(target.as_str()) {
violations.push(ContentViolation::BrokenReference {
from: from.clone(),
to: target.clone(),
});
}
}
}
violations
}
#[cfg(test)]
mod tests {
use super::*;
fn s(v: &str) -> String {
v.to_string()
}
fn owned(
parts: &[&str],
references: &[(&str, &[&str])],
) -> (Vec<String>, Vec<(String, Vec<String>)>) {
let parts = parts.iter().map(|f| s(f)).collect();
let references = references
.iter()
.map(|(name, targets)| (s(name), targets.iter().map(|t| s(t)).collect()))
.collect();
(parts, references)
}
fn run(parts: &[&str], references: &[(&str, &[&str])]) -> Vec<ContentViolation> {
let (parts, references) = owned(parts, references);
let graph = ContentGraph {
parts: &parts,
references: &references,
};
validate_content_references(&graph)
}
#[test]
fn empty_content_set_has_no_violations() {
assert!(run(&[], &[]).is_empty());
}
#[test]
fn lone_index_with_no_refs_is_clean() {
assert!(run(&["index"], &[("index", &[])]).is_empty());
}
#[test]
fn broken_ref_from_index_is_reported() {
let v = run(&["index"], &[("index", &["plan"])]);
assert_eq!(
v,
vec![ContentViolation::BrokenReference {
from: s("index"),
to: s("plan"),
}]
);
}
#[test]
fn broken_ref_from_companion_is_reported() {
let v = run(
&["index", "plan"],
&[("index", &[]), ("plan", &["mockup.png"])],
);
assert_eq!(
v,
vec![ContentViolation::BrokenReference {
from: s("plan"),
to: s("mockup.png"),
}]
);
}
#[test]
fn unreferenced_companions_are_not_orphans() {
assert!(run(&["index", "notes"], &[("index", &[]), ("notes", &[])]).is_empty());
}
#[test]
fn unreferenced_attachments_are_not_orphans() {
assert!(run(&["index", "mockup UX.xml"], &[("index", &[])]).is_empty());
}
}