use crate::Node;
#[must_use]
pub fn is_declared(header: &Node, tag: &str) -> bool {
header.all("SCHMA").any(|schma| {
schma
.all("TAG")
.any(|declaration| declaration.logical_value().split_whitespace().next() == Some(tag))
})
}
pub fn declare(header: &mut Node, tag: &str, uri: &str) -> bool {
if is_declared(header, tag) {
return false;
}
let declaration = Node::with_value("TAG", format!("{tag} {uri}"));
if let Some(schma) = header.first_mut("SCHMA") {
schma.push(declaration);
} else {
header.push(Node::new("SCHMA").child(declaration));
}
true
}
#[must_use]
pub fn declarations(header: &Node) -> Vec<(String, String)> {
let mut found = Vec::new();
for schma in header.all("SCHMA") {
for declaration in schma.all("TAG") {
let value = declaration.logical_value();
let mut parts = value.split_whitespace();
if let Some(tag) = parts.next() {
found.push((tag.to_owned(), parts.collect::<Vec<_>>().join(" ")));
}
}
}
found
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Document;
fn header() -> Node {
Node::new("HEAD").child(Node::new("GEDC").child(Node::with_value("VERS", "7.0")))
}
#[test]
fn a_declaration_is_written_once_and_only_once() {
let mut head = header();
assert!(declare(&mut head, "_EX", "https://where.example/EX"));
assert!(
!declare(&mut head, "_EX", "https://where.example/EX"),
"a second call must not write a second declaration"
);
assert!(declare(&mut head, "_OTHER", "https://where.example/OTHER"));
let text = Document {
records: vec![head],
}
.to_text();
assert_eq!(text.matches("2 TAG _EX ").count(), 1, "{text}");
assert_eq!(text.matches("1 SCHMA").count(), 1, "{text}");
assert!(text.contains("2 TAG _OTHER https://"), "{text}");
}
#[test]
fn a_declaration_already_in_the_file_is_left_exactly_as_it_is() {
let document = Document::parse(
"0 HEAD\n\
1 SCHMA\n\
2 TAG _EX https://somebody.example/their/own/uri\n\
0 TRLR\n",
)
.expect("parse");
let mut head = document.header().expect("header").clone();
assert!(!declare(&mut head, "_EX", "https://where.example/EX"));
let text = Document {
records: vec![head],
}
.to_text();
assert!(text.contains("somebody.example"), "{text}");
assert_eq!(text.matches("TAG _EX").count(), 1, "{text}");
}
#[test]
fn declarations_are_read_back_with_their_uris() {
let mut head = header();
assert!(declare(&mut head, "_EX", "https://where.example/EX"));
assert_eq!(
declarations(&head),
[("_EX".to_owned(), "https://where.example/EX".to_owned())]
);
assert!(!is_declared(&head, "_OTHER"));
assert!(is_declared(&head, "_EX"));
}
}