xml_paths/
lib.rs

1use quick_xml::events::Event;
2use quick_xml::Reader;
3use std::collections::HashSet;
4
5pub mod xpath;
6
7/// List all xml paths (deduplicated and sorted, where siblings have the same path if they have the same element type).
8pub fn paths(path: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
9    let mut reader = Reader::from_file(path)?;
10    let mut xpath: xpath::XPath = xpath::XPath::new();
11    let mut buf = Vec::new();
12    let mut xpath_strings = HashSet::new();
13    loop {
14        match reader.read_event(&mut buf) {
15            Ok(Event::Start(ref e)) => {
16                xpath.push(xpath::start_tag_string(e)?);
17            }
18            Ok(Event::End(ref e)) => {
19                xpath.pop_checked(xpath::end_tag_string(e)?);
20            }
21            Ok(Event::Eof) => break,
22            Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
23            _ => (),
24        }
25        xpath_strings.insert(xpath.as_string());
26    }
27    let mut xpath_strings: Vec<String> = xpath_strings.into_iter().collect();
28    xpath_strings.sort();
29    Ok(xpath_strings)
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn test_paths() {
38        let paths = paths("example_data/notes.xml").unwrap();
39        assert_eq!(
40            paths,
41            vec![
42                "/",
43                "/notes",
44                "/notes/note",
45                "/notes/note/body",
46                "/notes/note/from",
47                "/notes/note/heading",
48                "/notes/note/to"
49            ]
50        );
51    }
52}