# 機能設計書 58-依存関係インストール

## 概要

本ドキュメントは、serve-dのLSPカスタム通知 `served/installDependency` の機能設計を記述する。DUBレシピファイルに依存関係を追加し、dub upgradeとインポート更新を実行する機能である。

### 本機能の処理概要

**業務上の目的・背景**：D言語の開発では、サードパーティのDUBパッケージを依存関係として追加する場面が頻繁にある。本機能は、エディタ上から依存関係パッケージを追加する操作を自動化する。dub.sdlまたはdub.json形式のレシピファイルを直接編集して依存関係を追加し、不足パッケージのダウンロードとインポートパスの更新を一連の処理として実行する。

**機能の利用シーン**：エディタの依存関係管理UI（DUBツリービュー等）から新しいパッケージを追加する際に使用される。

**主要な処理内容**：
1. クライアントから `served/installDependency` 通知を受信する
2. 進捗レポートを開始する
3. `injectDependency` 関数でDUBレシピファイルに依存関係を追記する
4. DubComponentの `selectAndDownloadMissing()` で不足パッケージをダウンロードする
5. `updateImports()` でインポートパスを更新する
6. 進捗レポートを完了する

**関連システム・外部連携**：ファイルシステム上のDUBレシピファイル（dub.sdl/dub.json）を直接編集する。DubComponentを通じてパッケージダウンロードを実行する。updateImports（No.54）を内部呼出しする。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 58 | 依存関係インストール（served/installDependency） | 主画面 | DUBレシピファイルに依存関係を追加 |

## 機能種別

ファイル編集・状態更新処理（DUBレシピファイル編集、パッケージダウンロード、インポート更新）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | 追加するDUB依存関係のパッケージ名 | なし |
| version | string | Yes | インストールするバージョン（例: "1.0.0"）。レシピファイルには`~>{version}`形式で記録される | なし |

### 入力データソース

LSPクライアントからのJSON-RPC通知（InstallRequest構造体）

## 出力仕様

### 出力データ

本機能は通知（Notification）であるため、直接の戻り値はない。以下の副作用を生じる。

| 項目名 | 型 | 説明 |
|--------|-----|------|
| レシピファイル編集 | ファイルシステム | dub.sdlまたはdub.jsonに依存関係行を追加 |
| パッケージダウンロード | ファイルシステム | 不足パッケージをDUBのローカルキャッシュにダウンロード |
| インポートパス更新 | インメモリ | DCDとインデックスのインポートパスを更新 |

### 出力先

- ファイルシステム上のDUBレシピファイル
- 進捗通知によるUI更新

## 処理フロー

### 処理シーケンス

```
1. 通知受信
   └─ InstallRequestとしてデシリアライズ
2. 進捗レポート開始
   └─ ProgressType.importUpgrades (0/10)
3. 依存関係注入
   └─ injectDependency() でレシピファイルに依存関係を追記
       ├─ [dub.sdlの場合] トップレベルの最後のdependency行の直後に追記
       └─ [dub.jsonの場合] dependenciesブロック内に追記（ブロックがなければ新規作成）
4. 不足パッケージダウンロード
   └─ DubComponent.selectAndDownloadMissing() で不足分をダウンロード
5. 進捗レポート中間
   └─ ProgressType.dubReload (7/10)
6. インポートパス更新
   └─ updateImports(UpdateImportsParams(false)) で進捗通知なしで更新
7. 進捗レポート完了
   └─ ProgressType.dubReload (10/10)
```

### フローチャート

```mermaid
flowchart TD
    A[served/installDependency 通知受信] --> B[進捗レポート開始 0/10]
    B --> C[injectDependency でレシピ編集]
    C --> D{dub.sdl 存在?}
    D -->|Yes| E[SDL形式で依存関係追記]
    D -->|No| F{dub.json/package.json 存在?}
    F -->|Yes| G[JSON形式で依存関係追記]
    F -->|No| H[何もしない]
    E --> I{DubComponent あり?}
    G --> I
    H --> I
    I -->|Yes| J[selectAndDownloadMissing]
    I -->|No| K[進捗レポート 7/10]
    J --> K
    K --> L[updateImports 呼出]
    L --> M[進捗レポート完了 10/10]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | SDL/JSON自動判定 | dub.sdlが存在する場合はSDL形式、そうでない場合はdub.json/package.jsonを使用する | 常時 |
| BR-58-02 | バージョン接頭辞 | バージョンは `~>{version}` 形式で記録される（SDLは `version="~>{version}"`、JSONは `"~>{version}"`） | 常時 |
| BR-58-03 | 挿入位置（SDL） | トップレベル（depth==0）の最後のdependency行の直後に挿入する | dub.sdl使用時 |
| BR-58-04 | 挿入位置（JSON） | dependenciesブロック内の先頭に挿入する。ブロックがなければファイル末尾に新規作成する | dub.json使用時 |
| BR-58-05 | 改行コード検出 | ファイルの既存改行コード（CRLF/LF）を検出し、追記時にも同じ改行コードを使用する | 常時 |
| BR-58-06 | 深度追跡 | `{` と `}` の出現数で深度を追跡し、トップレベルのdependency行を正確に特定する | SDL/JSON共通 |

### 計算ロジック

特になし。

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

### 操作別データベース影響一覧

本機能はデータベースを使用しない。ファイルシステム上のDUBレシピファイルを直接編集する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無操作 | dub.sdl, dub.json, package.jsonいずれも存在しない場合 | 何もせずに次の処理に進む |

### リトライ仕様

リトライは不要。

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

トランザクション管理は不要。レシピファイルの書き込みはアトミックではないが、DUBの構文解析が失敗した場合でも元のファイルが上書きされるのみ。

## パフォーマンス要件

レシピファイルの編集はファイルシステムI/Oを伴う。selectAndDownloadMissingはネットワーク通信を伴うため遅延が発生する可能性がある。進捗通知で経過を報告する。

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

信頼されていないパッケージ名をレシピファイルに書き込むため、インジェクション攻撃のリスクがある。ただし、DUBの構文解析で不正なパッケージ名は拒否される。

## 備考

- `injectDependency` はdub.dファイル内のプライベート関数群の一つとして実装されている（623-725行目）
- 改行コード検出ロジックにより、Windowsの CRLF とUnixの LF の両方に対応している
- JSONのdependenciesブロックが存在しない場合、ファイル末尾のJSON閉じ括弧の前にdependenciesブロックごと新規作成する
- 空のJSONファイルの場合は、dependenciesブロックを含む最小限のJSONを新規作成する

---

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

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

### 推奨読解順序

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

InstallRequest構造体のフィールドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | protoext.d | `source/served/lsp/protoext.d` | InstallRequest構造体（99-106行目）のnameとversion_フィールドを確認 |

#### Step 2: エントリーポイントを理解する

installDependency関数の全体処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dub.d | `source/served/commands/dub.d` | installDependency関数（574-586行目）のエントリーポイント |

**主要処理フロー**:
1. **574行目**: `@protocolNotification("served/installDependency")` - LSP通知として登録
2. **577行目**: `activeInstance` - アクティブインスタンスの取得
3. **579行目**: `reportProgress(ProgressType.importUpgrades, 0, 10, uri)` - 進捗開始
4. **580行目**: `injectDependency(instance, req)` - レシピファイルへの依存関係注入
5. **582行目**: `instance.get!DubComponent.selectAndDownloadMissing()` - パッケージダウンロード
6. **584行目**: `updateImports(UpdateImportsParams(false))` - インポートパス更新
7. **585行目**: `reportProgress(ProgressType.dubReload, 10, 10, uri)` - 進捗完了

#### Step 3: 依存関係注入処理を理解する

injectDependency関数のSDL/JSON各形式への書き込みロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dub.d | `source/served/commands/dub.d` | injectDependency関数（623-725行目） |

**主要処理フロー**:
- **625-649行目**: SDL形式の処理。depth追跡でトップレベルのdependency行を探し、直後に挿入
- **651-724行目**: JSON形式の処理。dependenciesブロックの有無で3パターンに分岐

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

```
installDependency(InstallRequest) [source/served/commands/dub.d:575]
    |
    +-- activeInstance [source/served/types.d:471]
    |
    +-- reportProgress(...) [source/served/utils/progress.d:53]
    |
    +-- injectDependency(instance, req) [source/served/commands/dub.d:623]
    |       |
    |       +-- fs.exists(sdl) / fs.exists(json) [ファイル存在確認]
    |       +-- fs.readText / splitLines [ファイル読込・行分割]
    |       +-- 挿入位置計算 [depth追跡]
    |       +-- fs.write [ファイル書き込み]
    |
    +-- DubComponent.selectAndDownloadMissing() [workspace-d API]
    |
    +-- updateImports(UpdateImportsParams(false)) [source/served/commands/dub.d:213]
    |       |
    |       +-- DubComponent.update.getYield
    |       +-- DCDComponent.refreshImports()
    |       +-- backgroundIndex()
    |
    +-- reportProgress(完了)
```

### データフロー図

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

InstallRequest             -->  installDependency()                -->  レシピファイル編集
  name: string                   |                                       パッケージDL
  version: string                +-- injectDependency (FS編集)           インポートパス更新
                                 +-- selectAndDownloadMissing            進捗通知
                                 +-- updateImports
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dub.d | `source/served/commands/dub.d` | ソース | installDependency（574-586行目）、injectDependency（623-725行目） |
| protoext.d | `source/served/lsp/protoext.d` | ソース | InstallRequest構造体定義（99-106行目） |
| progress.d | `source/served/utils/progress.d` | ソース | reportProgress関数 |
| types.d | `source/served/types.d` | ソース | activeInstanceプロパティ、backend定義 |
| index.d | `source/served/commands/index.d` | ソース | backgroundIndex関数（updateImports経由で呼出） |
