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
//! JSON で書き出すところ
use folio::book::{Book, Sheet, Writer};
use folio::cell::{Cell, Row, Value};
use folio::json;
use folio::style::{Format, Style};
use std::path::PathBuf;
/// 試しに使うファイルの置き場。target の下なので消えても差し支えない
fn place(name: &str) -> PathBuf {
let mut path = PathBuf::from("target");
path.push(format!("json-{name}.xlsx"));
path
}
#[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::new(6, Value::Number(6.0));
total.formula = "SUM(C6:D6)".to_string();
let row = Row {
number: 6,
cell: vec![
Cell::new(1, Value::Text("01".to_string())),
Cell::new(2, Value::Empty),
Cell::new(3, Value::Number(171948.0)),
Cell::new(4, Value::Boolean(true)),
Cell::new(5, Value::Error("#N/A".to_string())),
total,
Cell::styled(7, Value::Number(46112.0), 1),
],
};
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_written_book_comes_back_as_json() {
let path = place("book");
let mut writer = Writer::create(&path, &["データ"]).unwrap();
writer.start("データ").unwrap();
writer.row(&["名前".into(), 3.5.into(), true.into()]).unwrap();
writer.place(3, &[Cell::new(1, "とびとび".into()), Cell::styled(2, 46112.0.into(), Format::Date.index())]).unwrap();
writer.finish().unwrap();
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()]);
}