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
137
138
139
//! XML を読むところ
use folio::xml::{Event, Reader, escape};
/// 読み取った事象を、確かめやすい 1 本の文字列にする
fn trace(source: &str) -> String {
let mut reader = Reader::over(source.as_bytes().to_vec());
let mut result = String::new();
while let Some(event) = reader.next().unwrap() {
match event {
Event::Open => result.push_str(&format!("<{}{}>", reader.name(), attribute(&reader))),
Event::Empty => result.push_str(&format!("<{}{}/>", reader.name(), attribute(&reader))),
Event::Close => result.push_str(&format!("</{}>", reader.name())),
Event::Text => result.push_str(&format!("[{}]", reader.text())),
}
}
result
}
/// 試しに使う属性だけを並べる
fn attribute(reader: &Reader) -> String {
let mut result = String::new();
for key in ["r", "t", "s", "id", "space", "val"] {
if let Some(value) = reader.attribute(key) {
result.push_str(&format!(" {key}={value}"));
}
}
result
}
#[test]
fn tags_and_text_come_back_in_order() {
assert_eq!(trace("<a>x<b/>y</a>"), "<a>[x]<b/>[y]</a>");
}
#[test]
fn the_declaration_and_comments_are_skipped() {
assert_eq!(trace("<?xml version=\"1.0\"?><!-- 注記 --><a>1</a>"), "<a>[1]</a>");
}
#[test]
fn a_namespace_prefix_is_dropped() {
let source = "<x:worksheet xmlns:x=\"n\"><x:row r=\"1\"/></x:worksheet>";
assert_eq!(trace(source), "<worksheet><row r=1/></worksheet>");
}
#[test]
fn an_attribute_prefix_is_dropped_too() {
assert_eq!(trace("<sheet r:id=\"rId1\"/>"), "<sheet id=rId1/>");
}
#[test]
fn entities_turn_back_into_characters() {
assert_eq!(trace("<t>a&b<c>d"e'f</t>"), "<t>[a&b<c>d\"e'f]</t>");
assert_eq!(trace("<t>Aあ</t>"), "<t>[Aあ]</t>");
}
#[test]
fn cdata_is_taken_as_it_is() {
assert_eq!(trace("<a><![CDATA[<b>&]]></a>"), "<a>[<b>&]</a>");
}
#[test]
fn a_closing_bracket_inside_a_value_is_not_the_end_of_the_tag() {
assert_eq!(trace("<a r=\"1>2\"/>"), "<a r=1>2/>");
}
#[test]
fn single_quotes_and_spaces_around_the_equal_sign_are_allowed() {
assert_eq!(trace("<a r = 'x' />"), "<a r=x/>");
}
#[test]
fn whitespace_between_tags_is_returned_as_text() {
assert_eq!(trace("<a>\n\t<b/>\n</a>"), "<a>[\n\t]<b/>[\n]</a>");
}
#[test]
fn a_long_document_is_read_across_the_pieces_it_arrives_in() {
let source = format!(
"<sheetData>{}</sheetData>",
(0..20000)
.map(|index| format!("<row r=\"{index}\"><c r=\"A{index}\" t=\"s\"><v>{index}</v></c></row>"))
.collect::<String>()
);
let mut reader = Reader::over(source.into_bytes());
let mut count = 0;
let mut last = String::new();
while let Some(event) = reader.next().unwrap() {
if event == Event::Open && reader.name() == "row" {
count += 1;
last = reader.attribute("r").unwrap_or("").to_string();
}
}
assert_eq!(count, 20000);
assert_eq!(last, "19999");
}
#[test]
fn text_that_is_not_utf8_is_an_error() {
let mut reader = Reader::over(vec![b'<', b'a', b'>', 0xff, 0xfe, b'<', b'/', b'a', b'>']);
reader.next().unwrap();
assert!(reader.next().is_err());
}
#[test]
fn an_unclosed_tag_is_an_error() {
let mut reader = Reader::over(b"<a".to_vec());
assert!(reader.next().is_err());
}
#[test]
fn escaping_replaces_what_xml_cannot_hold() {
assert_eq!(escape("a<b>&\"c"), "a<b>&"c");
assert_eq!(escape("行\tと\n改行"), "行\tと\n改行");
assert_eq!(escape("落とす\u{1}文字"), "落とす文字");
}