1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::wiki;
use quick_xml::events::Event;
use std::io::BufRead;
enum Tag {
Namespace,
Other,
Text,
Timestamp,
Title,
UserName,
}
pub fn parse<Callback: FnMut(&wiki::Page) -> ()>(stream: &mut dyn BufRead, mut callback: Callback) {
let mut xml_reader = quick_xml::Reader::from_reader(stream);
let mut buffer = Vec::new();
let mut current_tag = Tag::Other;
let mut current_namespace = 0;
let mut current_title: Option<String> = None;
let mut current_text: Option<String> = None;
let mut current_target: Option<String> = None;
let mut current_username: Option<String> = None;
let mut current_timestamp: Option<String> = None;
loop {
match xml_reader.read_event_into(&mut buffer) {
Ok(Event::Empty(ref event)) => {
match event.name().as_ref() {
b"redirect" => {
for attribute in event.attributes() {
match attribute {
Ok(attribute) => {
if attribute.key.as_ref() == b"title" {
let escaped_value = attribute.unescape_value();
current_target = Some(std::str::from_utf8(&escaped_value.unwrap().as_bytes()).unwrap().to_string());
}
}
Err(_) => (), // ignore bad attribute in the dump
}
}
current_tag = Tag::Other
},
_ => current_tag = Tag::Other,
}
},
Ok(Event::Start(ref event)) => {
match event.name().as_ref() {
b"ip" => current_tag = Tag::UserName,
b"ns" => current_tag = Tag::Namespace,
b"text" => current_tag = Tag::Text,
b"timestamp" => current_tag = Tag::Timestamp,
b"title" => current_tag = Tag::Title,
b"username" => current_tag = Tag::UserName,
_ => current_tag = Tag::Other,
}
},
Ok(Event::End(ref event)) => {
match event.name().as_ref() {
b"page" => {
let page = wiki::Page {
namespace: current_namespace,
title: current_title.unwrap(),
text: current_text,
target: current_target,
username: current_username,
timestamp: current_timestamp,
};
current_namespace = 0;
current_title = None;
current_text = None;
current_target = None;
current_username = None;
current_timestamp = None;
callback(&page);
current_tag = Tag::Other;
},
_ => current_tag = Tag::Other,
}
}
Ok(Event::Text(ref event)) => {
let escaped_event = event.unescape();
match current_tag {
Tag::Namespace => {
match escaped_event {
Ok(ref buffer) => {
current_namespace = std::str::from_utf8(buffer.as_bytes()).unwrap().parse().unwrap();
}
Err(_) => (), // ignore encoding error in the dump
}
},
Tag::Text => {
match escaped_event {
Ok(ref buffer) => {
current_text = Some(std::str::from_utf8(buffer.as_bytes()).unwrap().to_string());
}
Err(_) => (), // ignore encoding error in the dump
}
},
Tag::Timestamp => {
match escaped_event {
Ok(ref buffer) => {
current_timestamp = Some(std::str::from_utf8(buffer.as_bytes()).unwrap().to_string());
}
Err(_) => (), // ignore encoding error in the dump
}
},
Tag::Title => {
match escaped_event {
Ok(ref buffer) => {
current_title = Some(std::str::from_utf8(buffer.as_bytes()).unwrap().to_string());
}
Err(_) => (), // ignore encoding error in the dump
}
},
Tag::UserName => {
match escaped_event {
Ok(ref buffer) => {
current_username = Some(std::str::from_utf8(buffer.as_bytes()).unwrap().to_string());
},
Err(_) => (), // ignore encoding error in the dump
}
},
Tag::Other => (),
}
},
Err(error) => {
eprintln!("arkbot: XML parsing error at position {}: {:?}", xml_reader.buffer_position(), error);
break;
},
Ok(Event::Eof) => break,
_ => (),
}
buffer.clear();
}
}