# 機能設計書 89-ファイルURI変換

## 概要

本ドキュメントは、lua-language-serverにおけるファイルURI変換機能について記載する。この機能は、ファイルシステムパスとURI（Uniform Resource Identifier）間の相互変換を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Language Server Protocol (LSP) では、ファイルの識別にURIを使用する。一方、ファイルシステム操作にはOSネイティブのパスが必要となる。本機能は、これら2つの表現間の相互変換を提供し、Windows/Unix両方のパス形式に対応する。また、UNCパス（ネットワーク共有）やドライブレターの正規化も行う。

**機能の利用シーン**：LSPメッセージの受信時（URI→パス変換）、診断結果の送信時（パス→URI変換）、ファイル参照の解決、ワークスペースフォルダの管理など、ファイル識別が必要なすべての場面で利用される。

**主要な処理内容**：
1. パスからURIへのエンコード（encode）
2. URIからパスへのデコード（decode）
3. URIの分割（split）
4. URIの検証（isValid）
5. URIの正規化（normalize）
6. パーセントエンコーディング処理

**関連システム・外部連携**：bee.platformモジュールでOS判定を行う。ファイル管理モジュール（files.lua）やワークスペース管理（workspace/）から呼び出される。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たず、URI変換基盤として動作 |

## 機能種別

ユーティリティ / データ変換

## 入力仕様

### 入力パラメータ

#### m.encode関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string | Yes | ファイルシステムパス | 文字列であること |

#### m.decode関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | ファイルURI | 文字列であること |

#### m.split関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | ファイルURI | 文字列であること |

#### m.isValid関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | 検証対象のURI | 文字列であること |

### 入力データソース

- LSPメッセージ内のdocumentUri
- ファイルシステムからのパス
- ワークスペースフォルダ設定

## 出力仕様

### 出力データ

#### m.encode関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| uri | string | ファイルURI（file://形式） |

#### m.decode関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| path | string | ファイルシステムパス |

#### m.split関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| scheme | string | URIスキーム（file等） |
| authority | string | ホスト名（通常は空） |
| path | string | パス部分 |

#### m.isValid関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| valid | boolean | URIが有効かどうか |

### 出力先

- 呼び出し元へのURI/パス返却

## 処理フロー

### 処理シーケンス

```
encode処理（パス→URI）:
1. Windows環境ではバックスラッシュをスラッシュに変換
   └─ path:gsub('\\', '/')
2. UNCパスの処理
   └─ //で始まる場合、authorityを抽出
3. 先頭にスラッシュがなければ追加
   └─ path:sub(1, 1) ~= '/'
4. ドライブレターを小文字に変換
   └─ /C: → /c:
5. パーセントエンコーディング
   └─ 特殊文字をエスケープ
6. file://スキームを付与
   └─ 'file://' .. authority .. path

decode処理（URI→パス）:
1. URIを分割
   └─ scheme, authority, path
2. 各部分をパーセントデコード
   └─ normalize()
3. UNCパスの構築
   └─ authorityがあれば//authority/path
4. ドライブレターの処理
   └─ /c: → C:（Windows）
5. スラッシュをバックスラッシュに変換（Windows）
   └─ path:gsub('/', '\\')
```

### フローチャート

```mermaid
flowchart TD
    subgraph encode["encode処理（パス→URI）"]
        E1[パス受け取り] --> E2{Windows?}
        E2 -->|Yes| E3[バックスラッシュ変換]
        E2 -->|No| E4{UNCパス?}
        E3 --> E4
        E4 -->|Yes| E5[authority抽出]
        E4 -->|No| E6{先頭スラッシュ?}
        E5 --> E6
        E6 -->|No| E7[スラッシュ追加]
        E6 -->|Yes| E8{ドライブレター?}
        E7 --> E8
        E8 -->|Yes| E9[小文字化]
        E8 -->|No| E10[パーセントエンコード]
        E9 --> E10
        E10 --> E11[file://付与]
        E11 --> E12[URI返却]
    end

    subgraph decode["decode処理（URI→パス）"]
        D1[URI受け取り] --> D2[split呼び出し]
        D2 --> D3{scheme/path有効?}
        D3 -->|No| D4[空文字返却]
        D3 -->|Yes| D5[パーセントデコード]
        D5 --> D6{authority有り?}
        D6 -->|Yes| D7[UNCパス構築]
        D6 -->|No| D8{ドライブレター?}
        D7 --> D9{Windows?}
        D8 -->|Yes| D10[大文字化・スラッシュ削除]
        D8 -->|No| D9
        D10 --> D9
        D9 -->|Yes| D11[バックスラッシュ変換]
        D9 -->|No| D12[パス返却]
        D11 --> D12
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-89-01 | Windowsパス変換 | バックスラッシュとスラッシュを相互変換 | platform.os == 'windows' |
| BR-89-02 | ドライブレター正規化 | エンコード時は小文字、デコード時は大文字 | /X:形式のパス |
| BR-89-03 | UNCパス対応 | //server/share形式をfile://server/share/pathに変換 | //で始まるパス |
| BR-89-04 | パーセントエンコード | 英数字、-_.~/以外をエスケープ | URI構築時 |
| BR-89-05 | file:スキームのみ有効 | scheme != 'file'は無効なURI | isValid判定時 |
| BR-89-06 | 空パスは無効 | path == ''は無効なURI | isValid判定時 |

### 計算ロジック

パーセントエンコード:
```lua
local escPatt = '[^%w%-%.%_%~%/]'
local function esc(c)
    return ('%%%02X'):format(c:byte())
end
path:gsub(escPatt, esc)
```

パーセントデコード:
```lua
local function normalize(str)
    return str:gsub('%%(%x%x)', function(n)
        return string.char(tonumber(n, 16))
    end)
end
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 空文字列返却 | 不正なURI形式 | decodeは空文字列を返却 |
| - | false返却 | 無効なURI | isValidはfalseを返却 |

### リトライ仕様

リトライは行わない。不正な入力に対しては空文字列またはfalseを返却する。

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

本機能はトランザクションを使用しない。

## パフォーマンス要件

- URI変換は高頻度で呼び出されるため、効率的に実装
- 正規表現パターンはコンパイル済みを再利用

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

- パストラバーサル攻撃への対策は呼び出し元で行う
- 外部入力のURIは検証後に使用

## 備考

本機能はRFC 3986に準拠したURI処理を行う。ただし、実用性を重視してfile:スキームに特化した実装となっている。

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

URI変換で使用される定数とヘルパー関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | file-uri.lua | `script/file-uri.lua` | 定数とヘルパー関数（1-16行目） |

**読解のコツ**:
- `escPatt`: パーセントエンコード対象パターン
- `esc`: パーセントエンコード関数
- `normalize`: パーセントデコード関数

#### Step 2: encode関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | file-uri.lua | `script/file-uri.lua` | encode関数（26-60行目） |

**主要処理フロー**:
1. **28-30行目**: Windowsでバックスラッシュ変換
2. **32-44行目**: UNCパス処理（//で始まる場合）
3. **46-48行目**: 先頭スラッシュ追加
4. **51-54行目**: ドライブレター小文字化
5. **56-59行目**: パーセントエンコードとスキーム付与

#### Step 3: decode関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | file-uri.lua | `script/file-uri.lua` | decode関数（69-89行目） |

**主要処理フロー**:
- **70-73行目**: URIパターンマッチング
- **74-76行目**: パーセントデコード
- **77-79行目**: UNCパス構築
- **80-81行目**: ドライブレター処理
- **85-87行目**: Windowsでバックスラッシュ変換

#### Step 4: ユーティリティ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | file-uri.lua | `script/file-uri.lua` | split/isValid/normalize関数（91-118行目） |

**主要処理フロー**:
- **91-93行目**: split - URIを3部分に分割
- **97-109行目**: isValid - スキーム・パスの検証
- **111-116行目**: normalize - encode(decode(uri))

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

```
m.encode(path)
    │
    ├─ [Windows] path:gsub('\\', '/')
    ├─ UNCパス処理
    │      └─ authority抽出
    ├─ 先頭スラッシュ追加
    ├─ ドライブレター小文字化
    └─ 'file://' .. authority:gsub(escPatt, esc) .. path:gsub(escPatt, esc)

m.decode(uri)
    │
    ├─ uri:match('([^:]*):?/?/?([^/]*)(.*)') → scheme, authority, path
    ├─ normalize(scheme/authority/path)
    │      └─ gsub('%%(%x%x)', tonumber → char)
    ├─ UNCパス構築 or ドライブレター処理
    └─ [Windows] value:gsub('/', '\\')

m.isValid(uri)
    │
    ├─ m.split(uri)
    └─ scheme == 'file' and path ~= ''
```

### データフロー図

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

encode:
"/usr/home"
    │
    └───▶ 先頭スラッシュ確認 ───▶ パーセントエンコード ───▶ "file:///usr/home"

"C:\my\files"
    │
    └───▶ スラッシュ変換 ───▶ 小文字化 ───▶ エンコード ───▶ "file:///c%3A/my/files"

"\\server\share\path"
    │
    └───▶ authority抽出 ───▶ パス構築 ───▶ "file://server/share/path"

decode:
"file:///c%3A/my/files"
    │
    └───▶ split ───▶ normalize ───▶ ドライブ処理 ───▶ "C:\my\files"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| file-uri.lua | `script/file-uri.lua` | ソース | ファイルURI変換の実装 |
| files.lua | `script/files.lua` | ソース | ファイル管理（URI変換を使用） |
| workspace/ | `script/workspace/` | ソース | ワークスペース管理（URI変換を使用） |
| bee.platform | `(外部モジュール)` | ライブラリ | OS判定 |
