use oxml::{ErrorKind, parse};
const BROKEN: &[(&str, &str)] = &[
("<a>text", "a tag left open"),
("<a></b>", "mismatched tags"),
("<a>&nope;</a>", "an entity nobody declared"),
("<a x='1' x='2'/>", "the same attribute twice"),
("<p:a/>", "a prefix with no declaration"),
("<a/><b/>", "two root elements"),
("", "nothing at all"),
("<a>]]></a>", "a literal CDATA terminator"),
("<!-- no -- good -->", "a double hyphen in a comment"),
];
fn main() {
for (input, why) in BROKEN {
let Err(error) = parse(input) else {
println!("{input:?} unexpectedly parsed");
continue;
};
let (line, column) = error.line_column(input);
println!("{why}:");
println!(" {input:?}");
println!(" {error}");
println!(" line {line}, column {column}");
let advice = match &error.kind {
ErrorKind::UnexpectedEof => "the document is truncated",
ErrorKind::MismatchedEndTag { expected, found } => {
&format!("close <{expected}>, not <{found}>")
}
ErrorKind::UnknownEntity(name) => {
&format!("declare &{name}; or write &{name};")
}
ErrorKind::DuplicateAttribute(name) => {
&format!("remove one of the two {name} attributes")
}
ErrorKind::UnboundPrefix(p) => &format!("add an xmlns:{p}"),
ErrorKind::TrailingContent => "wrap both elements in a root",
ErrorKind::NoRootElement => "an XML document needs one element",
ErrorKind::IllegalCdataEnd => "write ]]> instead",
ErrorKind::MalformedComment => "-- may not appear in a comment",
_ => "see the message above",
};
println!(" advice: {advice}\n");
}
let input =
"<config>\n <name>ok</name>\n <port>8080</hostname>\n</config>";
if let Err(error) = parse(input) {
let (line, column) = error.line_column(input);
let source = input.lines().nth(line - 1).unwrap_or_default();
println!("== pointing at the problem ==");
println!("{line:>3} | {source}");
println!(" | {}^ {}", " ".repeat(column - 1), error.kind);
}
}