xlsxzero 0.1.0

Pure-Rust Excel parser and Markdown converter for RAG systems
Documentation
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
# **インターフェース設計書 (API設計書)**

## **目的**

本設計書は、xlsxzeroクレートの公開APIを厳密に定義し、利用者が直接呼び出す「契約(Contract)」を明確化する。この設計書は**プログラム開発において最も重要な文書**であり、一度公開すると変更が非常に困難になるため、慎重に設計する。

**主要な目標:**
* 利用者が直感的かつ安全にプログラムを使用できるAPIを提供する
* 型システムを活用し、コンパイル時に多くのエラーを検出できる設計にする
* Rustエコシステムの慣習に従った、エルゴノミックなAPIを提供する

**関連文書:**
* [requirements.md](requirements.md): 要件定義
* [architecture.md](architecture.md): アーキテクチャ設計

---

## **1. 公開API一覧**

### **1.1. 名前空間・モジュール構成**

```rust
xlsxzero
├── ConverterBuilder          // ビルダーパターンによる変換設定の構築
├── Converter                 // 変換処理のエントリーポイント
├── XlsxToMdError             // エラー型
├── MergeStrategy             // セル結合戦略の列挙型
├── DateFormat                // 日付形式の列挙型
├── FormulaMode               // 数式出力モードの列挙型
└── SheetSelector             // シート選択方式の列挙型
```

### **1.2. 公開構造体・列挙型一覧**

| 名前               | 種類   | 概要                                       |
| :----------------- | :----- | :----------------------------------------- |
| `ConverterBuilder` | 構造体 | 変換設定を段階的に構築するビルダー         |
| `Converter`        | 構造体 | Excel→Markdown変換処理を実行するファサード |
| `XlsxToMdError`    | 列挙型 | クレート全体のエラー型                     |
| `MergeStrategy`    | 列挙型 | セル結合の処理戦略                         |
| `DateFormat`       | 列挙型 | 日付の出力形式                             |
| `FormulaMode`      | 列挙型 | 数式の出力モード                           |
| `SheetSelector`    | 列挙型 | シート選択方式                             |

---

## **2. 各APIの詳細定義**

### **2.1. ConverterBuilder構造体**

#### **概要**
Fluent Builder APIを提供し、`Converter`インスタンスを段階的に構築する。すべての設定項目にデフォルト値が設定されており、利用者は必要な設定のみをオーバーライド可能。

#### **コンストラクタ**

##### `ConverterBuilder::new() -> Self`

**シグネチャ:**
```rust
pub fn new() -> Self
```

**概要:**
デフォルト設定を持つビルダーインスタンスを生成する。

**戻り値:**
* `ConverterBuilder`: デフォルト設定済みのビルダー

**デフォルト設定:**
* シート選択: すべてのシート
* セル結合戦略: データ重複フィル
* 日付形式: ISO 8601 (YYYY-MM-DD)
* 非表示要素: スキップ
* 数式モード: キャッシュ値を出力

**使用例:**
```rust
use xlsxzero::ConverterBuilder;

let builder = ConverterBuilder::new();
```

---

#### **設定メソッド**

##### `with_sheet_selector(self, selector: SheetSelector) -> Self`

**シグネチャ:**
```rust
pub fn with_sheet_selector(self, selector: SheetSelector) -> Self
```

**概要:**
変換対象のシートを選択する。

**引数:**
* `selector: SheetSelector`: シート選択方式
  * `SheetSelector::All`: すべてのシートを変換(デフォルト)
  * `SheetSelector::Index(usize)`: インデックス指定(0始まり)
  * `SheetSelector::Name(String)`: シート名指定
  * `SheetSelector::Indices(Vec<usize>)`: 複数のインデックス指定
  * `SheetSelector::Names(Vec<String>)`: 複数のシート名指定

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

// 単一シートをインデックスで指定
let builder = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Index(0));

// 単一シートを名前で指定
let builder = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Name("Sheet1".to_string()));

// 複数シートを指定
let builder = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Indices(vec![0, 2, 4]));
```

---

##### `with_merge_strategy(self, strategy: MergeStrategy) -> Self`

**シグネチャ:**
```rust
pub fn with_merge_strategy(self, strategy: MergeStrategy) -> Self
```

**概要:**
セル結合の処理戦略を指定する。

**引数:**
* `strategy: MergeStrategy`: セル結合戦略
  * `MergeStrategy::DataDuplication`: 結合セル範囲内に親セルの値を複製(デフォルト)
  * `MergeStrategy::HtmlFallback`: HTMLテーブル(rowspan/colspan)として出力

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, MergeStrategy};

let builder = ConverterBuilder::new()
    .with_merge_strategy(MergeStrategy::HtmlFallback);
```

---

##### `with_date_format(self, format: DateFormat) -> Self`

**シグネチャ:**
```rust
pub fn with_date_format(self, format: DateFormat) -> Self
```

**概要:**
日付の出力形式を指定する。

**引数:**
* `format: DateFormat`: 日付形式
  * `DateFormat::Iso8601`: ISO 8601形式(YYYY-MM-DD)(デフォルト)
  * `DateFormat::Custom(String)`: カスタム形式(chrono互換フォーマット文字列)

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, DateFormat};

// ISO 8601形式(デフォルト)
let builder = ConverterBuilder::new()
    .with_date_format(DateFormat::Iso8601);

// カスタム形式
let builder = ConverterBuilder::new()
    .with_date_format(DateFormat::Custom("%Y年%m月%d日".to_string()));
```

---

##### `with_formula_mode(self, mode: FormulaMode) -> Self`

**シグネチャ:**
```rust
pub fn with_formula_mode(self, mode: FormulaMode) -> Self
```

**概要:**
数式セルの出力モードを指定する。

**引数:**
* `mode: FormulaMode`: 数式出力モード
  * `FormulaMode::CachedValue`: キャッシュされた結果値を出力(デフォルト)
  * `FormulaMode::Formula`: 数式文字列を出力

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, FormulaMode};

let builder = ConverterBuilder::new()
    .with_formula_mode(FormulaMode::Formula);
```

---

##### `include_hidden(self, include: bool) -> Self`

**シグネチャ:**
```rust
pub fn include_hidden(self, include: bool) -> Self
```

**概要:**
非表示要素(非表示シート、行、列)を出力に含めるかを指定する。

> **⚠️ Phase I制限事項:**
> Phase Iでは `calamine` ライブラリの制限により、非表示行・非表示列の情報を取得できません。
> そのため、`include_hidden(false)` を指定しても**非表示行・列のフィルタリングは機能しません**。
> 非表示シートのみがフィルタリング対象となります。
> Phase IIで `xl/worksheets/sheet*.xml` から `hidden="1"` 属性を直接パースすることで完全対応予定です。

**引数:**
* `include: bool`:
  * `true`: 非表示要素を含める
  * `false`: 非表示要素をスキップ(デフォルト)
    * **Phase I**: 非表示シートのみスキップ
    * **Phase II**: 非表示行・列もスキップ

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::ConverterBuilder;

let builder = ConverterBuilder::new()
    .include_hidden(true);
```

---

##### `with_range(self, start: (u32, u32), end: (u32, u32)) -> Self`

**シグネチャ:**
```rust
pub fn with_range(self, start: (u32, u32), end: (u32, u32)) -> Self
```

**概要:**
処理対象のセル範囲を制限する。範囲外のセルは無視される。

**引数:**
* `start: (u32, u32)`: 開始セル座標 (row, col)(0始まり)
* `end: (u32, u32)`: 終了セル座標 (row, col)(0始まり)

**制約:**
* `start.0 <= end.0` かつ `start.1 <= end.1` でなければならない
* 制約違反の場合、`build()`時に`XlsxToMdError::Config`を返す

**戻り値:**
* `Self`: メソッドチェーン用のビルダー

**使用例:**
```rust
use xlsxzero::ConverterBuilder;

// A1:C10の範囲を処理(0始まりなので、row 0-9, col 0-2)
let builder = ConverterBuilder::new()
    .with_range((0, 0), (9, 2));
```

---

#### **ビルドメソッド**

##### `build(self) -> Result<Converter, XlsxToMdError>`

**シグネチャ:**
```rust
pub fn build(self) -> Result<Converter, XlsxToMdError>
```

**概要:**
設定を検証し、`Converter`インスタンスを生成する。

**戻り値:**
* `Ok(Converter)`: 設定が有効な場合、Converterインスタンス
* `Err(XlsxToMdError::Config)`: 設定が無効な場合(例: 範囲指定の開始 > 終了)

**発生し得るエラー:**
* `XlsxToMdError::Config(String)`: 設定の検証に失敗した場合
  * 範囲指定の開始座標が終了座標より大きい
  * カスタム日付形式が不正な書式文字列

**使用例:**
```rust
use xlsxzero::ConverterBuilder;

let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Index(0))
    .build()?;
```

---

### **2.2. Converter構造体**

#### **概要**
Excel→Markdown変換処理を実行するファサード。`ConverterBuilder`から生成される。

#### **変換メソッド**

##### `convert<R: Read + Seek, W: Write>(&self, input: R, output: W) -> Result<(), XlsxToMdError>`

**シグネチャ:**
```rust
pub fn convert<R: Read + Seek, W: Write>(
    &self,
    input: R,
    output: W
) -> Result<(), XlsxToMdError>
```

**概要:**
Excelファイルを読み込み、Markdown形式に変換して出力する。ストリーミング処理により、大規模ファイルでもメモリ効率的に変換可能。

**型パラメータ:**
* `R: Read + Seek`: 入力ソース(`std::io::Read`および`std::io::Seek`トレイトを実装する型)
  * **Seekが必須な理由:** XLSXファイルはZIP形式で、ZIP Central Directoryがファイル末尾に配置されています。`calamine` は ZIP アーカイブを解析する際、まずファイル末尾の Central Directory を読み取り、その後個々のエントリ(`xl/workbook.xml`, `xl/worksheets/sheet1.xml` など)の位置へシークする必要があるため、`Seek` トレイトが必須です。順次読み取り専用(`Read` のみ)では ZIP 解析が不可能です。
  * 対応する型: `std::fs::File`, `std::io::Cursor<Vec<u8>>`, `std::io::Cursor<&[u8]>`
  * 非対応の型: `std::io::stdin()`, `TcpStream`, `BufReader<TcpStream>` など(Seekが実装されていない)
* `W: Write`: 出力先(`std::io::Write`トレイトを実装する型)

**引数:**
* `input: R`: Excelファイルの読み取りソース
  * `std::fs::File`: ファイルシステムから読み込む場合
  * `std::io::Cursor<Vec<u8>>`: カーソル付きバッファから読み込む場合(メモリバッファ)
  * `std::io::Cursor<&[u8]>`: 読み取り専用バッファから読み込む場合
* `output: W`: Markdown出力先
  * `std::fs::File`: ファイルシステムへ書き込む場合
  * `Vec<u8>`: メモリバッファへ書き込む場合
  * `std::io::stdout()`: 標準出力へ書き込む場合

**戻り値:**
* `Ok(())`: 変換が成功した場合
* `Err(XlsxToMdError)`: 変換中にエラーが発生した場合

**発生し得るエラー:**
* `XlsxToMdError::Io`: I/Oエラー(ファイル読み込み/書き込み失敗)
* `XlsxToMdError::Parse`: Excelファイルの解析に失敗
* `XlsxToMdError::Config`: 指定されたシート名が存在しない
* `XlsxToMdError::UnsupportedFeature`: 未サポートの機能に遭遇

**処理フロー:**
1. 入力ストリームからExcelファイルを解析(calamine使用)
2. 指定されたシートを選択
3. セルデータをストリーミング処理で抽出
4. 書式適用(日付変換、数値フォーマット)
5. セル結合処理(指定された戦略に基づく)
6. Markdownテーブルとして出力ストリームへ書き込み
7. 出力バッファをフラッシュ

**使用例:**
```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;

// ファイル→ファイルの変換
let converter = ConverterBuilder::new().build()?;
let input = File::open("input.xlsx")?;
let output = File::create("output.md")?;
converter.convert(input, output)?;

// メモリバッファからの変換
use std::io::Cursor;
let excel_data: Vec<u8> = vec![/* ... */];
let mut markdown_output = Vec::new();
converter.convert(Cursor::new(excel_data), &mut markdown_output)?;

// 標準出力への変換
let input = File::open("input.xlsx")?;
converter.convert(input, std::io::stdout())?;
```

---

##### `convert_to_string<R: Read + Seek>(&self, input: R) -> Result<String, XlsxToMdError>`

**シグネチャ:**
```rust
pub fn convert_to_string<R: Read + Seek>(
    &self,
    input: R
) -> Result<String, XlsxToMdError>
```

**概要:**
Excelファイルを読み込み、Markdown形式の文字列として返す。内部的に`convert()`を呼び出し、メモリバッファに出力する便利メソッド。

**型パラメータ:**
* `R: Read + Seek`: 入力ソース(`std::io::Read`および`std::io::Seek`トレイトを実装する型)

**引数:**
* `input: R`: Excelファイルの読み取りソース

**戻り値:**
* `Ok(String)`: 変換されたMarkdown文字列
* `Err(XlsxToMdError)`: 変換中にエラーが発生した場合

**発生し得るエラー:**
* `convert()`と同じエラー(詳細は上記参照)

**使用例:**
```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;

let converter = ConverterBuilder::new().build()?;
let input = File::open("input.xlsx")?;
let markdown = converter.convert_to_string(input)?;
println!("{}", markdown);
```

---

### **2.3. XlsxToMdError列挙型**

#### **概要**
クレート全体で使用される構造化エラー型。`thiserror`クレートを使用して実装され、自動的に`std::error::Error`トレイトを実装する。

#### **定義**

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum XlsxToMdError {
    /// I/Oエラー(ファイル操作や入出力ストリームの問題)
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// Excelファイルの解析エラー
    #[error("Failed to parse Excel file: {0}")]
    Parse(#[from] calamine::Error),

    /// 設定エラー(不正なシート名や範囲指定)
    #[error("Configuration error: {0}")]
    Config(String),

    /// 未サポート機能エラー
    #[error("Unsupported feature at sheet '{sheet}', cell {cell}: {message}")]
    UnsupportedFeature {
        sheet: String,
        cell: String,
        message: String,
    },
}
```

#### **エラーバリアントの詳細**

##### `XlsxToMdError::Io(std::io::Error)`

**発生条件:**
* 入力ファイルが存在しない
* 出力ファイルへの書き込み権限がない
* ディスク容量不足
* ネットワークストリームの接続エラー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, XlsxToMdError};
use std::fs::File;

let converter = ConverterBuilder::new().build()?;

match converter.convert(
    File::open("nonexistent.xlsx")?,
    std::io::stdout()
) {
    Ok(_) => println!("変換成功"),
    Err(XlsxToMdError::Io(e)) => eprintln!("I/Oエラー: {}", e),
    Err(e) => eprintln!("その他のエラー: {}", e),
}
```

---

##### `XlsxToMdError::Parse(calamine::Error)`

**発生条件:**
* ファイルが有効なExcel形式ではない
* ファイルが破損している
* ZIPアーカイブ構造が不正
* XMLパースエラー

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, XlsxToMdError};

let converter = ConverterBuilder::new().build()?;
let broken_data = b"This is not an Excel file";

match converter.convert(&broken_data[..], std::io::stdout()) {
    Ok(_) => println!("変換成功"),
    Err(XlsxToMdError::Parse(e)) => eprintln!("解析エラー: {}", e),
    Err(e) => eprintln!("その他のエラー: {}", e),
}
```

---

##### `XlsxToMdError::Config(String)`

**発生条件:**
* 指定されたシート名が存在しない
* 指定されたシートインデックスが範囲外
* セル範囲の指定が不正(開始 > 終了)
* カスタム日付形式が不正

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector, XlsxToMdError};
use std::fs::File;

let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Name("NonExistentSheet".to_string()))
    .build()?;

match converter.convert(File::open("input.xlsx")?, std::io::stdout()) {
    Ok(_) => println!("変換成功"),
    Err(XlsxToMdError::Config(msg)) => eprintln!("設定エラー: {}", msg),
    Err(e) => eprintln!("その他のエラー: {}", e),
}
```

---

##### `XlsxToMdError::UnsupportedFeature { sheet, cell, message }`

**発生条件:**
* サポート範囲外の複雑な書式に遭遇
* 未実装の機能(例: ピボットテーブル、マクロ)
* 深くネストされた構造(セキュリティ制限)

**フィールド:**
* `sheet: String`: エラーが発生したシート名
* `cell: String`: エラーが発生したセル座標(例: "A1")
* `message: String`: エラーの詳細説明

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, XlsxToMdError};
use std::fs::File;

let converter = ConverterBuilder::new().build()?;

match converter.convert(File::open("complex.xlsx")?, std::io::stdout()) {
    Ok(_) => println!("変換成功"),
    Err(XlsxToMdError::UnsupportedFeature { sheet, cell, message }) => {
        eprintln!("未サポート機能: シート '{}', セル {}: {}", sheet, cell, message);
    },
    Err(e) => eprintln!("その他のエラー: {}", e),
}
```

---

### **2.4. MergeStrategy列挙型**

#### **概要**
セル結合の処理戦略を定義する列挙型。

#### **定義**

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeStrategy {
    /// 結合セル範囲内のすべてのセルに親セルの値を複製
    /// 純粋なMarkdownテーブルとして出力(デフォルト)
    DataDuplication,

    /// HTMLテーブル(rowspan/colspan属性)として出力
    /// 構造的忠実性を維持
    HtmlFallback,
}
```

#### **バリアントの詳細**

##### `MergeStrategy::DataDuplication`

**動作:**
* 結合セル範囲(例: A1:C1)を検出
* 親セル(A1)の値を取得
* 結合範囲内のすべてのセルに親セルの値を複製
* 純粋なMarkdownテーブルとして出力

**利点:**
* LLMが理解しやすい
* トークン効率が高い
* RAGシステムに最適

**欠点:**
* 視覚的な結合情報が失われる

**出力例:**
```markdown
| Header1 | Header1 | Header1 |
| ------- | ------- | ------- |
| Data1   | Data2   | Data3   |
```

---

##### `MergeStrategy::HtmlFallback`

**動作:**
* 結合セルを検出した場合、テーブル全体をHTMLとして出力
* `<td rowspan="...">`および`<td colspan="...">`属性を使用

**利点:**
* 構造的忠実性を完全に維持
* 視覚的な結合情報を保持

**欠点:**
* トークン数が増加
* LLMによる解析が複雑になる可能性

**出力例:**
```html
<table>
  <tr>
    <th colspan="3">Header1</th>
  </tr>
  <tr>
    <td>Data1</td>
    <td>Data2</td>
    <td>Data3</td>
  </tr>
</table>
```

---

### **2.5. DateFormat列挙型**

#### **概要**
日付の出力形式を定義する列挙型。

#### **定義**

```rust
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DateFormat {
    /// ISO 8601形式(YYYY-MM-DD)(デフォルト)
    Iso8601,

    /// カスタム形式(chrono互換フォーマット文字列)
    Custom(String),
}
```

#### **バリアントの詳細**

##### `DateFormat::Iso8601`

**出力形式:** `YYYY-MM-DD`(例: `2025-11-20`)

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, DateFormat};

let converter = ConverterBuilder::new()
    .with_date_format(DateFormat::Iso8601)
    .build()?;
```

---

##### `DateFormat::Custom(String)`

**概要:**
chrono互換のフォーマット文字列を使用して、カスタム日付形式を指定する。

**フォーマット指定子(主要なもの):**
* `%Y`: 4桁の年(例: 2025)
* `%y`: 2桁の年(例: 25)
* `%m`: 2桁の月(01-12)
* `%d`: 2桁の日(01-31)
* `%B`: 月の完全名(例: January)
* `%b`: 月の省略名(例: Jan)

**詳細:** [chrono::format::strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) を参照。

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, DateFormat};

// 日本語形式
let converter = ConverterBuilder::new()
    .with_date_format(DateFormat::Custom("%Y年%m月%d日".to_string()))
    .build()?;
// 出力例: 2025年11月20日

// US形式
let converter = ConverterBuilder::new()
    .with_date_format(DateFormat::Custom("%m/%d/%Y".to_string()))
    .build()?;
// 出力例: 11/20/2025
```

---

### **2.6. FormulaMode列挙型**

#### **概要**
数式セルの出力モードを定義する列挙型。

#### **定義**

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FormulaMode {
    /// キャッシュされた結果値を出力(デフォルト)
    CachedValue,

    /// 数式文字列を出力
    Formula,
}
```

#### **バリアントの詳細**

##### `FormulaMode::CachedValue`

**動作:**
* Excelファイルにキャッシュされた数式の結果値を出力
* 数式を評価しない(評価エンジンを持たないため)

**利点:**
* 安定した処理
* 追加の依存関係不要

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, FormulaMode};

let converter = ConverterBuilder::new()
    .with_formula_mode(FormulaMode::CachedValue)
    .build()?;
```

**出力例:**
Excelセル: `=SUM(A1:A10)` → キャッシュ値: `123.45`
Markdown出力: `123.45`

---

##### `FormulaMode::Formula`

**動作:**
* 数式文字列をそのまま出力
* 数式を評価しない

**利点:**
* 数式の構造を保持
* デバッグや監査に有用

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, FormulaMode};

let converter = ConverterBuilder::new()
    .with_formula_mode(FormulaMode::Formula)
    .build()?;
```

**出力例:**
Excelセル: `=SUM(A1:A10)` → Markdown出力: `=SUM(A1:A10)`

---

### **2.7. SheetSelector列挙型**

#### **概要**
変換対象のシートを選択する方式を定義する列挙型。

#### **定義**

```rust
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SheetSelector {
    /// すべてのシートを変換(デフォルト)
    All,

    /// 単一シートをインデックスで指定(0始まり)
    Index(usize),

    /// 単一シートを名前で指定
    Name(String),

    /// 複数シートをインデックスで指定
    Indices(Vec<usize>),

    /// 複数シートを名前で指定
    Names(Vec<String>),
}
```

#### **バリアントの詳細**

##### `SheetSelector::All`

**動作:**
* ワークブック内のすべてのシートを順番に変換
* 非表示シートは`include_hidden()`設定に従う

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::All)
    .build()?;
```

---

##### `SheetSelector::Index(usize)`

**動作:**
* 指定されたインデックスのシートのみを変換
* インデックスは0始まり

**エラー:**
* インデックスが範囲外の場合、`XlsxToMdError::Config`を返す

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

// 最初のシートのみを変換
let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Index(0))
    .build()?;
```

---

##### `SheetSelector::Name(String)`

**動作:**
* 指定された名前のシートのみを変換
* 名前は大文字小文字を区別

**エラー:**
* 指定された名前のシートが存在しない場合、`XlsxToMdError::Config`を返す

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Name("Sales Data".to_string()))
    .build()?;
```

---

##### `SheetSelector::Indices(Vec<usize>)`

**動作:**
* 指定された複数のインデックスのシートを順番に変換
* インデックスは0始まり

**エラー:**
* いずれかのインデックスが範囲外の場合、`XlsxToMdError::Config`を返す

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

// 1番目、3番目、5番目のシートを変換
let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Indices(vec![0, 2, 4]))
    .build()?;
```

---

##### `SheetSelector::Names(Vec<String>)`

**動作:**
* 指定された複数の名前のシートを順番に変換
* 名前は大文字小文字を区別

**エラー:**
* いずれかの名前のシートが存在しない場合、`XlsxToMdError::Config`を返す

**使用例:**
```rust
use xlsxzero::{ConverterBuilder, SheetSelector};

let converter = ConverterBuilder::new()
    .with_sheet_selector(SheetSelector::Names(vec![
        "Sheet1".to_string(),
        "Sheet3".to_string(),
    ]))
    .build()?;
```

---

## **3. 使用例(Code Snippet)**

### **3.1. 基本的な使用例**

```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // デフォルト設定でConverterを構築
    let converter = ConverterBuilder::new().build()?;

    // ファイルからファイルへの変換
    let input = File::open("input.xlsx")?;
    let output = File::create("output.md")?;
    converter.convert(input, output)?;

    println!("変換完了!");
    Ok(())
}
```

---

### **3.2. カスタム設定を使用した変換**

```rust
use xlsxzero::{ConverterBuilder, SheetSelector, MergeStrategy, DateFormat};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // カスタム設定でConverterを構築
    let converter = ConverterBuilder::new()
        .with_sheet_selector(SheetSelector::Index(0))  // 最初のシートのみ
        .with_merge_strategy(MergeStrategy::DataDuplication)  // データ重複フィル
        .with_date_format(DateFormat::Custom("%Y年%m月%d日".to_string()))  // 日本語形式
        .include_hidden(false)  // 非表示要素をスキップ
        .build()?;

    let input = File::open("sales_data.xlsx")?;
    let output = File::create("sales_data.md")?;
    converter.convert(input, output)?;

    println!("変換完了!");
    Ok(())
}
```

---

### **3.3. メモリバッファからの変換**

```rust
use xlsxzero::ConverterBuilder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Excelデータをメモリ上で取得(例: HTTPレスポンスから)
    let excel_data: Vec<u8> = fetch_excel_from_api()?;

    // Converterを構築
    let converter = ConverterBuilder::new().build()?;

    // Markdown文字列として変換
    let markdown = converter.convert_to_string(&excel_data[..])?;

    println!("変換結果:\n{}", markdown);
    Ok(())
}

fn fetch_excel_from_api() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    // HTTPリクエストでExcelファイルを取得する実装
    Ok(vec![/* ... */])
}
```

---

### **3.4. 複数シートの変換**

```rust
use xlsxzero::{ConverterBuilder, SheetSelector};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 複数のシートを名前で指定
    let converter = ConverterBuilder::new()
        .with_sheet_selector(SheetSelector::Names(vec![
            "Summary".to_string(),
            "Details".to_string(),
            "Appendix".to_string(),
        ]))
        .build()?;

    let input = File::open("report.xlsx")?;
    let output = File::create("report.md")?;
    converter.convert(input, output)?;

    println!("複数シートの変換完了!");
    Ok(())
}
```

---

### **3.5. セル範囲を限定した変換**

```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // A1:E20の範囲のみを変換
    let converter = ConverterBuilder::new()
        .with_range((0, 0), (19, 4))  // 0始まり(row 0-19, col 0-4)
        .build()?;

    let input = File::open("large_spreadsheet.xlsx")?;
    let output = File::create("summary.md")?;
    converter.convert(input, output)?;

    println!("指定範囲の変換完了!");
    Ok(())
}
```

---

### **3.6. HTMLフォールバック戦略を使用した変換**

```rust
use xlsxzero::{ConverterBuilder, MergeStrategy};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // HTMLフォールバック戦略で結合セルを正確に表現
    let converter = ConverterBuilder::new()
        .with_merge_strategy(MergeStrategy::HtmlFallback)
        .build()?;

    let input = File::open("complex_table.xlsx")?;
    let output = File::create("complex_table.md")?;
    converter.convert(input, output)?;

    println!("HTMLフォールバックで変換完了!");
    Ok(())
}
```

---

### **3.7. エラーハンドリングの実装例**

```rust
use xlsxzero::{ConverterBuilder, XlsxToMdError, SheetSelector};
use std::fs::File;

fn main() {
    if let Err(e) = convert_excel() {
        match e {
            XlsxToMdError::Io(io_err) => {
                eprintln!("I/Oエラー: {}", io_err);
                eprintln!("ファイルが存在するか、権限があるか確認してください。");
            }
            XlsxToMdError::Parse(parse_err) => {
                eprintln!("解析エラー: {}", parse_err);
                eprintln!("ファイルが有効なExcel形式か確認してください。");
            }
            XlsxToMdError::Config(msg) => {
                eprintln!("設定エラー: {}", msg);
                eprintln!("シート名や範囲指定を確認してください。");
            }
            XlsxToMdError::UnsupportedFeature { sheet, cell, message } => {
                eprintln!("未サポート機能: シート '{}', セル {}", sheet, cell);
                eprintln!("詳細: {}", message);
            }
        }
        std::process::exit(1);
    }
}

fn convert_excel() -> Result<(), XlsxToMdError> {
    let converter = ConverterBuilder::new()
        .with_sheet_selector(SheetSelector::Name("Data".to_string()))
        .build()?;

    let input = File::open("input.xlsx")?;
    let output = File::create("output.md")?;
    converter.convert(input, output)?;

    println!("変換成功!");
    Ok(())
}
```

---

### **3.8. 数式文字列を出力する例**

```rust
use xlsxzero::{ConverterBuilder, FormulaMode};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 数式文字列をそのまま出力
    let converter = ConverterBuilder::new()
        .with_formula_mode(FormulaMode::Formula)
        .build()?;

    let input = File::open("financial_model.xlsx")?;
    let output = File::create("financial_model.md")?;
    converter.convert(input, output)?;

    println!("数式を含む変換完了!");
    Ok(())
}
```

---

### **3.9. 非表示要素を含めた変換**

```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 非表示の行・列・シートも含めて変換
    let converter = ConverterBuilder::new()
        .include_hidden(true)
        .build()?;

    let input = File::open("complete_data.xlsx")?;
    let output = File::create("complete_data.md")?;
    converter.convert(input, output)?;

    println!("非表示要素を含む変換完了!");
    Ok(())
}
```

---

### **3.10. CLIツールでの使用例**

```rust
use xlsxzero::{ConverterBuilder, SheetSelector, XlsxToMdError};
use std::fs::File;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() < 3 {
        eprintln!("使い方: {} <input.xlsx> <output.md> [sheet_index]", args[0]);
        std::process::exit(1);
    }

    let input_path = &args[1];
    let output_path = &args[2];
    let sheet_index = args.get(3).and_then(|s| s.parse::<usize>().ok());

    match convert_excel(input_path, output_path, sheet_index) {
        Ok(_) => println!("変換完了: {} -> {}", input_path, output_path),
        Err(e) => {
            eprintln!("エラー: {}", e);
            std::process::exit(1);
        }
    }
}

fn convert_excel(
    input_path: &str,
    output_path: &str,
    sheet_index: Option<usize>,
) -> Result<(), XlsxToMdError> {
    let mut builder = ConverterBuilder::new();

    if let Some(index) = sheet_index {
        builder = builder.with_sheet_selector(SheetSelector::Index(index));
    }

    let converter = builder.build()?;

    let input = File::open(input_path)?;
    let output = File::create(output_path)?;
    converter.convert(input, output)?;

    Ok(())
}
```

---

## **4. API設計の方針と慣習**

### **4.1. Rust慣習への準拠**

* **命名規則:**
  * 構造体: UpperCamelCase(例: `ConverterBuilder`)
  * 列挙型: UpperCamelCase(例: `MergeStrategy`)
  * メソッド: snake_case(例: `with_sheet_selector`)
  * 定数: SCREAMING_SNAKE_CASE(必要に応じて)

* **所有権とライフタイム:**
  * ビルダーメソッドは`self`を消費し、新しい`Self`を返す(ムーブセマンティクス)
  * `convert()`メソッドは`&self`を受け取り、複数回呼び出し可能

* **エラーハンドリング:**
  * すべての公開APIは`Result<T, XlsxToMdError>`を返す
  * `thiserror`による構造化エラー
  * `#[from]`属性による自動変換

### **4.2. 型安全性**

* **コンパイル時検証:**
  * ビルダーパターンにより、設定の組み合わせをコンパイル時に検証
  * ジェネリクスを活用し、任意の`Read`/`Write`トレイト実装型に対応

* **ランタイム検証:**
  * `build()`時に設定の妥当性を検証
  * 不正な設定は`XlsxToMdError::Config`として早期に検出

### **4.3. 拡張性**

* **列挙型の拡張:**
  * 列挙型に`#[non_exhaustive]`属性を付与し、将来のバリアント追加に対応
  * セマンティックバージョニングに従い、破壊的変更を最小化

* **トレイトベースの設計:**
  * 将来的にカスタム戦略を実装可能な設計を検討

---

## **5. バージョニングとAPI安定性**

### **5.1. セマンティックバージョニング**

本クレートは[Semantic Versioning 2.0.0](https://semver.org/)に従う。

* **MAJOR(破壊的変更):**
  * 公開APIのシグネチャ変更
  * 列挙型のバリアント削除
  * デフォルト動作の変更

* **MINOR(機能追加):**
  * 新しい公開API追加
  * 列挙型への新しいバリアント追加(`#[non_exhaustive]`の場合)
  * 新しいオプション設定の追加

* **PATCH(バグ修正):**
  * 内部実装のバグ修正
  * パフォーマンス改善
  * ドキュメント修正

### **5.2. 安定性保証**

* **v1.0.0以降:**
  * 公開APIは厳格な後方互換性を保証
  * 破壊的変更は次のメジャーバージョンでのみ実施

* **v0.x.x(開発段階):**
  * APIは流動的であり、マイナーバージョンで破壊的変更が発生する可能性

---

## **6. パフォーマンス特性**

### **6.1. メモリ使用量**

* **目標:** ピークメモリ使用量をファイルサイズの10%以下に抑制
* **実現手段:** ストリーミング処理とBufWriterの使用

### **6.2. 処理速度**

* **目標:**
  * 10MB以下のファイル: 1秒以内
  * バッチ処理: 1分あたり50ファイル以上

### **6.3. I/O効率**

* **自動最適化:** `convert()`メソッドは内部的にBufWriterを使用し、システムコール数を最小化
* **柔軟性:** `Read`/`Write`トレイトにより、ファイル、メモリ、ネットワークストリームに対応

---

## **7. スレッドセーフティ**

### **7.1. Converterのスレッドセーフティ**

* `Converter`は`Send` + `Sync`を実装
* 複数のスレッドから同時に`convert()`を呼び出し可能(各スレッドは独立した入出力ストリームを使用)

**使用例:**
```rust
use xlsxzero::ConverterBuilder;
use std::fs::File;
use std::sync::Arc;
use std::thread;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let converter = Arc::new(ConverterBuilder::new().build()?);

    let mut handles = vec![];

    for i in 0..4 {
        let converter = Arc::clone(&converter);
        let handle = thread::spawn(move || {
            let input = File::open(format!("input{}.xlsx", i)).unwrap();
            let output = File::create(format!("output{}.md", i)).unwrap();
            converter.convert(input, output).unwrap();
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("並列変換完了!");
    Ok(())
}
```

---

## **付録A: エラーメッセージ一覧**

| エラーバリアント     | メッセージフォーマット                                                 | 発生条件                                     |
| :------------------- | :--------------------------------------------------------------------- | :------------------------------------------- |
| `Io`                 | `"IO error: {原因}"`                                                   | ファイル操作失敗、権限不足、ディスク容量不足 |
| `Parse`              | `"Failed to parse Excel file: {原因}"`                                 | 不正なExcel形式、破損ファイル                |
| `Config`             | `"Configuration error: {詳細}"`                                        | 不正なシート名、範囲指定エラー               |
| `UnsupportedFeature` | `"Unsupported feature at sheet '{シート名}', cell {セル座標}: {詳細}"` | 未サポート機能、複雑な書式                   |

---

## **付録B: 推奨される使用パターン**

### **B.1. RAGシステムへの統合**

```rust
use xlsxzero::{ConverterBuilder, MergeStrategy};

fn process_excel_for_rag(
    excel_path: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    // RAG最適化された設定
    let converter = ConverterBuilder::new()
        .with_merge_strategy(MergeStrategy::DataDuplication)  // LLMフレンドリー
        .include_hidden(false)  // ノイズ削減
        .build()?;

    let input = std::fs::File::open(excel_path)?;
    let markdown = converter.convert_to_string(input)?;

    Ok(markdown)
}
```

### **B.2. バッチ処理パイプライン**

```rust
use xlsxzero::ConverterBuilder;
use std::fs;
use std::path::Path;

fn batch_convert_directory(
    input_dir: &Path,
    output_dir: &Path,
) -> Result<usize, Box<dyn std::error::Error>> {
    let converter = ConverterBuilder::new().build()?;
    let mut count = 0;

    for entry in fs::read_dir(input_dir)? {
        let entry = entry?;
        let path = entry.path();

        if path.extension().and_then(|s| s.to_str()) == Some("xlsx") {
            let output_path = output_dir.join(
                path.file_stem().unwrap()
            ).with_extension("md");

            let input = fs::File::open(&path)?;
            let output = fs::File::create(&output_path)?;
            converter.convert(input, output)?;

            count += 1;
        }
    }

    Ok(count)
}
```

---

**文書管理情報:**
* 作成日: 2025-11-20
* バージョン: 1.0
* 関連文書: [requirements.md](requirements.md), [architecture.md](architecture.md)