1 #![forbid(unsafe_code)]
2 
3 use std::cmp;
4 use std::collections::HashSet;
5 use std::env;
6 use std::fs::File;
7 use std::io::{self, BufReader, Read};
8 
9 use xml::reader::XmlEvent;
10 use xml::ParserConfig;
11 
main() -> Result<(), Box<dyn std::error::Error>>12 fn main() -> Result<(), Box<dyn std::error::Error>> {
13     let mut file;
14     let mut stdin;
15     let source: &mut dyn Read = if let Some(file_name) = env::args().nth(1) {
16         file = File::open(file_name).map_err(|e| format!("Cannot open input file: {e}"))?;
17         &mut file
18     } else {
19         stdin = io::stdin();
20         &mut stdin
21     };
22 
23     let reader = ParserConfig::new()
24         .whitespace_to_characters(true)
25         .ignore_comments(false)
26         .create_reader(BufReader::new(source));
27 
28     let mut processing_instructions = 0;
29     let mut elements = 0;
30     let mut character_blocks = 0;
31     let mut cdata_blocks = 0;
32     let mut characters = 0;
33     let mut comment_blocks = 0;
34     let mut comment_characters = 0;
35     let mut namespaces = HashSet::new();
36     let mut depth = 0;
37     let mut max_depth = 0;
38 
39     for e in reader {
40         let e = e.map_err(|e| format!("Error parsing XML document: {e}"))?;
41         match e {
42             XmlEvent::StartDocument { version, encoding, standalone } =>
43                 println!(
44                     "XML document version {}, encoded in {}, {}standalone",
45                     version, encoding, if standalone.unwrap_or(false) { "" } else { "not " }
46                 ),
47             XmlEvent::EndDocument => println!("Document finished"),
48             XmlEvent::ProcessingInstruction { .. } => processing_instructions += 1,
49             XmlEvent::Whitespace(_) => {} // can't happen due to configuration
50             XmlEvent::Characters(s) => {
51                 character_blocks += 1;
52                 characters += s.len();
53             }
54             XmlEvent::CData(s) => {
55                 cdata_blocks += 1;
56                 characters += s.len();
57             }
58             XmlEvent::Comment(s) => {
59                 comment_blocks += 1;
60                 comment_characters += s.len();
61             }
62             XmlEvent::StartElement { namespace, .. } => {
63                 depth += 1;
64                 max_depth = cmp::max(max_depth, depth);
65                 elements += 1;
66                 namespaces.extend(namespace.0.into_values());
67             }
68             XmlEvent::EndElement { .. } => {
69                 depth -= 1;
70             }
71         };
72     }
73 
74     namespaces.remove(xml::namespace::NS_EMPTY_URI);
75     namespaces.remove(xml::namespace::NS_XMLNS_URI);
76     namespaces.remove(xml::namespace::NS_XML_URI);
77 
78     println!("Elements: {elements}, maximum depth: {max_depth}");
79     println!("Namespaces (excluding built-in): {}", namespaces.len());
80     println!("Characters: {characters}, characters blocks: {character_blocks}, CDATA blocks: {cdata_blocks}");
81     println!("Comment blocks: {comment_blocks}, comment characters: {comment_characters}");
82     println!("Processing instructions (excluding built-in): {processing_instructions}");
83 
84     Ok(())
85 }
86