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
//! JSON で書き出すところ
mod common;
use common::{Workbook, styles};
use folio::book::{Book, Sheet};
use folio::cell::{Cell, Row, Value};
use folio::json;
use folio::style::Style;
use std::path::PathBuf;
/// 試しに使うファイルの置き場。target の下なので消えても差し支えない
fn place(name: &str) -> PathBuf {
let mut path = PathBuf::from("target");
path.push(format!("json-{name}.xlsx"));
path
}
/// セルを作る
fn cell(column: u32, style: u32, value: Value) -> Cell {
Cell {column, style, value, formula: String::new()}
}
#[test]
fn text_escapes_quotes_backslashes_and_control_characters() {
let mut result = String::new();
json::text_into("引用符\"と逆斜線\\と改行\nとタブ\tと制御\u{1}", &mut result);
assert_eq!(result, "\"引用符\\\"と逆斜線\\\\と改行\\nとタブ\\tと制御\\u0001\"");
}
#[test]
fn numbers_drop_the_needless_fraction_and_infinity_becomes_null() {
let mut result = String::new();
for number in [171948.0, -3.5, 0.25, f64::INFINITY, f64::NAN] {
json::number_into(number, &mut result);
result.push(' ');
}
assert_eq!(result, "171948 -3.5 0.25 null null ");
}
#[test]
fn a_row_keeps_the_type_of_each_value() {
let mut style = Style::new();
style.put_xf(0);
style.put_xf(14);
let mut total = cell(6, 0, Value::Number(6.0));
total.formula = "SUM(C6:D6)".to_string();
let row = Row {
number: 6,
cell: vec![
cell(1, 0, Value::Text("01".to_string())),
cell(2, 0, Value::Empty),
cell(3, 0, Value::Number(171948.0)),
cell(4, 0, Value::Boolean(true)),
cell(5, 0, Value::Error("#N/A".to_string())),
total,
cell(7, 1, Value::Number(46112.0)),
],
};
let expected = concat!(
"{\"number\":6,\"cell\":[",
"{\"column\":1,\"letter\":\"A\",\"value\":\"01\"},",
"{\"column\":3,\"letter\":\"C\",\"value\":171948},",
"{\"column\":4,\"letter\":\"D\",\"value\":true},",
"{\"column\":5,\"letter\":\"E\",\"error\":\"#N/A\"},",
"{\"column\":6,\"letter\":\"F\",\"value\":6,\"formula\":\"SUM(C6:D6)\"},",
"{\"column\":7,\"letter\":\"G\",\"value\":46112,\"date\":\"2026-03-31T00:00:00\"}",
"]}"
);
assert_eq!(json::row(&row, &style, false), expected);
}
#[test]
fn a_sheet_is_its_name_and_whether_it_is_shown() {
let one = Sheet {name: "隠し \"表\"".to_string(), part: String::new(), visible: false};
assert_eq!(json::sheet(&one), "{\"name\":\"隠し \\\"表\\\"\",\"visible\":false}");
}
#[test]
fn a_book_comes_back_as_json() {
let path = place("book");
Workbook::new()
.sheet("データ", concat!(
"<sheetData>",
"<row r=\"1\"><c r=\"A1\" t=\"s\"><v>0</v></c><c r=\"B1\"><v>3.5</v></c><c r=\"C1\" t=\"b\"><v>1</v></c></row>",
"<row r=\"3\"><c r=\"A3\" t=\"inlineStr\"><is><t>とびとび</t></is></c><c r=\"B3\" s=\"1\"><v>46112</v></c></row>",
"</sheetData>",
))
.shared(&["<t>名前</t>"])
.style(&styles(&[], &[0, 14]))
.write(&path);
let mut book = Book::open(&path).unwrap();
let epoch1904 = book.epoch1904();
let mut sheet = book.read("データ").unwrap();
let style = sheet.style();
let mut line = Vec::new();
while let Some(row) = sheet.read().unwrap() {
line.push(json::row(row, &style, epoch1904));
}
let first = concat!(
"{\"number\":1,\"cell\":[",
"{\"column\":1,\"letter\":\"A\",\"value\":\"名前\"},",
"{\"column\":2,\"letter\":\"B\",\"value\":3.5},",
"{\"column\":3,\"letter\":\"C\",\"value\":true}",
"]}"
);
let second = concat!(
"{\"number\":3,\"cell\":[",
"{\"column\":1,\"letter\":\"A\",\"value\":\"とびとび\"},",
"{\"column\":2,\"letter\":\"B\",\"value\":46112,\"date\":\"2026-03-31T00:00:00\"}",
"]}"
);
assert_eq!(line, vec![first.to_string(), second.to_string()]);
}