# 機能設計書 48-Excel/ODS

## 概要

本ドキュメントは、Nushellにおけるスプレッドシート形式のデータ読み込み機能（`from xlsx`および`from ods`コマンド）の詳細設計を記載する。これらのコマンドはMicrosoft Excel形式（.xlsx）およびOpenDocument Spreadsheet形式（.ods）のファイルをNushellのテーブルに変換する。

### 本機能の処理概要

スプレッドシート変換機能は、バイナリ形式のExcel/ODSファイルを読み込み、シート単位でレコード形式に変換する。各シートは「シート名: テーブル」のレコード構造で出力される。

**業務上の目的・背景**：Excelは業務データの標準的なフォーマットとして広く使用されている。スプレッドシートのデータをコマンドラインで処理・分析するニーズは多く、シェルからExcel/ODSファイルを直接操作できることは業務効率化に有用である。

**機能の利用シーン**：
- Excelレポートのデータ抽出
- 複数シートのデータ統合
- スプレッドシートデータのCSV変換
- バッチ処理でのExcelデータ加工

**主要な処理内容**：
1. `from xlsx`: Excelバイナリをテーブルに変換
2. `from xlsx --sheets`: 特定シートのみを変換
3. `from ods`: ODSバイナリをテーブルに変換
4. `from ods --sheets`: 特定シートのみを変換

**関連システム・外部連携**：calamineクレート（スプレッドシートパーサー）を使用。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLI専用機能のため画面なし |

## 機能種別

データ変換 / フォーマット変換

## 入力仕様

### 入力パラメータ

#### from xlsx

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | Binary | Yes | Excelバイナリ（パイプライン入力） | 有効なXLSX形式 |
| --sheets / -s | list\<string\> | No | 変換対象シート名のリスト | - |

#### from ods

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | Binary | Yes | ODSバイナリ（パイプライン入力） | 有効なODS形式 |
| --sheets / -s | list\<string\> | No | 変換対象シート名のリスト | - |

### 入力データソース

パイプライン入力（Binary型またはByteStream）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | record | シート名をキーとしたレコード |

出力構造:
```
{
  Sheet1: [
    {column0: value, column1: value, ...},
    {column0: value, column1: value, ...}
  ],
  Sheet2: [
    {column0: value, column1: value, ...}
  ]
}
```

カラム名は「column0」「column1」のように自動付番される。

### 出力先

標準出力（パイプライン出力）

## 処理フロー

### 処理シーケンス

```
from xlsx / from ods:
1. パイプライン入力を受け取る
   └─ Binary型またはByteStreamを期待
2. バイナリデータを収集
   └─ collect_binary関数
3. calamineでスプレッドシートをオープン
   └─ Xlsx::new または Ods::new
4. シート名リストを取得
   └─ --sheetsで指定があればフィルタ
5. 各シートを処理
   └─ worksheet_range関数
6. 各行をレコードに変換
   └─ セル値を型変換
7. シート名をキーとしたレコード出力
   └─ IndexMapで順序保持
```

### フローチャート

```mermaid
flowchart TD
    subgraph from_xlsx_ods ["from xlsx / from ods"]
        A1[入力: Binary] --> B1[collect_binary]
        B1 --> C1[Xlsx::new / Ods::new]
        C1 --> D1{オープン成功?}
        D1 -->|No| E1[エラー: Could not load file]
        D1 -->|Yes| F1[sheet_names取得]
        F1 --> G1{--sheets指定?}
        G1 -->|Yes| H1[フィルタ適用]
        G1 -->|No| I1[全シート]
        H1 --> J1[シートごとにループ]
        I1 --> J1
        J1 --> K1[worksheet_range]
        K1 --> L1{シート読込成功?}
        L1 -->|No| M1[エラー: Could not load sheet]
        L1 -->|Yes| N1[行ごとにレコード変換]
        N1 --> O1{全シート完了?}
        O1 -->|No| J1
        O1 -->|Yes| P1[record出力]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | シート単位出力 | シート名をキーとしたレコードで出力 | from xlsx/ods |
| BR-48-02 | カラム自動命名 | カラム名は「column{index}」形式 | from xlsx/ods |
| BR-48-03 | シートフィルタ | --sheetsで指定シートのみ変換 | --sheets指定時 |
| BR-48-04 | 空セル処理 | 空セルはnothingに変換 | from xlsx/ods |
| BR-48-05 | 日時変換 | XLSXの日時はローカルタイムゾーンで解釈 | from xlsx |
| BR-48-06 | 日時非対応 | ODSの日時はnothingに変換（未サポート） | from ods |
| BR-48-07 | シート順序保持 | IndexMapでシートの順序を保持 | from xlsx/ods |

### 計算ロジック

**セル型マッピング**:
| スプレッドシート | Nushell |
|----------------|---------|
| Empty | nothing |
| String | string |
| Float | float |
| Int | int |
| Bool | bool |
| DateTime (XLSX) | date |
| DateTime (ODS) | nothing |
| その他 | nothing |

**タイムゾーン処理** (XLSXのみ):
- ローカルタイムゾーンを使用
- `Local.timestamp_opt(0, 0)`でタイムゾーンオフセットを取得
- 取得失敗時はUTC固定

## データベース操作仕様

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UnsupportedInput | ファイル読み込み失敗 | 不正なXLSX/ODS形式 | ファイル形式を確認 |
| UnsupportedInput | シート読み込み失敗 | シートが存在しないまたは破損 | シート名を確認 |
| UnsupportedInput | バイナリ入力期待 | Binary以外の入力 | Binary入力を使用 |
| IncompatibleParametersSingle | 不正なシート名 | --sheetsに文字列以外 | 文字列リストを使用 |

### リトライ仕様

リトライは不要。

## トランザクション仕様

本機能はトランザクション処理を行わない。

## パフォーマンス要件

- calamineはメモリ内でスプレッドシートを処理
- 大きなファイルではメモリ使用量が増加
- ByteStreamから直接読み込み可能

## セキュリティ考慮事項

- バイナリ入力の検証（不正なファイル形式の検出）
- マクロは実行されない（データのみ読み込み）

## 備考

- calamineクレートはXLSX、ODS、XLS等複数形式に対応
- 本機能はXLSXとODSのみサポート（XLSは非対応）
- 書き込み機能（to xlsx/to ods）は提供されていない

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: コマンドインターフェースを理解する

コマンドの定義とパラメータを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | xlsx.rs | `crates/nu-command/src/formats/from/xlsx.rs` | FromXlsx構造体（8-68行目） |
| 1-2 | ods.rs | `crates/nu-command/src/formats/from/ods.rs` | FromOds構造体（7-67行目） |

**読解のコツ**: --sheetsパラメータで特定シートを指定できること、入力はBinary型であることを理解する。

#### Step 2: バイナリ入力の収集処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | xlsx.rs | `crates/nu-command/src/formats/from/xlsx.rs` | collect_binary関数（85-111行目） |

**主要処理フロー**:
- **86-87行目**: ByteStreamからバイト収集
- **88-110行目**: PipelineDataからバイト収集

#### Step 3: スプレッドシート変換処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | xlsx.rs | `crates/nu-command/src/formats/from/xlsx.rs` | from_xlsx関数（113-188行目） |
| 3-2 | ods.rs | `crates/nu-command/src/formats/from/ods.rs` | from_ods関数（113-175行目） |

**主要処理フロー**:
- **119-126行目**: Xlsx::new/Ods::newでスプレッドシートをオープン
- **130-133行目**: シート名リストの取得とフィルタリング
- **135-138行目** (xlsx): ローカルタイムゾーンの取得
- **140-182行目**: 各シートの変換処理
- **144-168行目**: セル値の型変換

### プログラム呼び出し階層図

```
from xlsx
    │
    ├─ FromXlsx::run (33-52行目)
    │      ├─ convert_columns (--sheets処理)
    │      └─ from_xlsx (113-188行目)
    │             ├─ collect_binary (85-111行目)
    │             │      └─ ByteStream/PipelineData → Vec<u8>
    │             ├─ Xlsx::new (calamine)
    │             ├─ sheet_names() + フィルタ
    │             ├─ Local.timestamp_opt (タイムゾーン取得)
    │             └─ 各シートごとにループ
    │                    ├─ worksheet_range
    │                    └─ rows().map(セル変換)
    │                           ├─ Data::Empty → Value::nothing
    │                           ├─ Data::String → Value::string
    │                           ├─ Data::Float → Value::float
    │                           ├─ Data::Int → Value::int
    │                           ├─ Data::Bool → Value::bool
    │                           └─ Data::DateTime → Value::date

from ods
    │
    ├─ FromOds::run (32-51行目)
    │      ├─ convert_columns (--sheets処理)
    │      └─ from_ods (113-175行目)
    │             ├─ collect_binary (84-111行目)
    │             ├─ Ods::new (calamine)
    │             ├─ sheet_names() + フィルタ
    │             └─ 各シートごとにループ
    │                    ├─ worksheet_range
    │                    └─ rows().map(セル変換)
    │                           ├─ Data::Empty → Value::nothing
    │                           ├─ Data::String → Value::string
    │                           ├─ Data::Float → Value::float
    │                           ├─ Data::Int → Value::int
    │                           ├─ Data::Bool → Value::bool
    │                           └─ _ → Value::nothing (DateTime含む)
```

### データフロー図

```
[入力]               [処理]                    [出力]

from xlsx:
Binary ───▶ collect_binary ───▶ Xlsx::new ───▶ worksheet_range ───▶ record
                 │                    │                │
                 └── ByteStream ──────┤                └── セル変換
                                      └── シートフィルタ     (DateTime→date)

from ods:
Binary ───▶ collect_binary ───▶ Ods::new ───▶ worksheet_range ───▶ record
                 │                    │                │
                 └── ByteStream ──────┤                └── セル変換
                                      └── シートフィルタ     (DateTime→nothing)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| xlsx.rs | `crates/nu-command/src/formats/from/xlsx.rs` | ソース | from xlsxコマンド実装 |
| ods.rs | `crates/nu-command/src/formats/from/ods.rs` | ソース | from odsコマンド実装 |
