# 機能設計書 90-プラグインシステム

## 概要

本ドキュメントは、Nushellのプラグインシステムの設計について記述する。この機能は、外部のRustプログラムをNushellのコマンドとして動的に追加する拡張機構を提供し、プラグインのライフサイクル管理、通信プロトコル、ガーベージコレクションなどを実現する。

### 本機能の処理概要

プラグインシステムは、Plugin/PluginCommandトレイトを実装した外部バイナリをNushellに登録し、コマンドとして実行できるようにする。プラグインは独立したプロセスとして動作し、MsgPackまたはJSONによるシリアライズされたメッセージでNushellエンジンと通信する。PersistentPluginによる永続化管理、PluginGcによる自動停止機能も備える。

**業務上の目的・背景**：Nushellのコア機能では対応できない特殊な処理や、サードパーティ製ライブラリを活用した機能拡張を可能にする。プラグインはRustで実装されるため、パフォーマンスとメモリ安全性を維持しながら柔軟な拡張が可能。

**機能の利用シーン**：`plugin add`でプラグイン登録、`plugin list`で一覧表示、`plugin rm`で削除、プラグインコマンドの実行など。

**主要な処理内容**：
1. プラグインの登録（`plugin add`）
2. プラグインの起動とインターフェース確立
3. コマンド実行要求の送受信
4. カスタム値のシリアライズ/デシリアライズ
5. プラグインのガーベージコレクション
6. プラグイン設定の管理

**関連システム・外部連携**：プラグインバイナリ（nu_plugin_*）、ファイルシステム（plugin.msgpackz）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインREPL | 補助機能 | 外部プラグインによる機能拡張（plugin add/rm/list） |

## 機能種別

プラグイン管理 / 拡張機能

## 入力仕様

### 入力パラメータ

#### plugin add コマンド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| plugin | Path | Yes | プラグインバイナリのパス | 存在確認 |
| --shell (-s) | Path | No | プラグイン実行用シェル | - |

#### plugin list コマンド

パラメータなし

#### plugin rm コマンド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | プラグイン名 | 登録済み確認 |

### 入力データソース

- プラグインバイナリファイル
- ~/.config/nushell/plugin.msgpackz（プラグイン登録情報）
- $env.config.plugins（プラグイン設定）
- $env.config.plugin_gc（GC設定）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| name | String | プラグイン名 |
| version | String | バージョン |
| status | String | running/stopped |
| pid | Int | プロセスID（実行中のみ） |
| filename | Path | プラグインバイナリパス |
| shell | Path | 実行シェル（オプション） |
| commands | List<String> | 提供コマンド一覧 |

### 出力先

- 標準出力（plugin list時）
- plugin.msgpackz（登録情報の永続化）

## 処理フロー

### 処理シーケンス

```
1. プラグイン登録
   └─ plugin add → シグネチャ取得 → plugin.msgpackz更新
2. プラグイン起動
   └─ コマンド実行時 → PersistentPlugin.get() → spawn()
3. コマンド実行
   └─ PluginInterface.run() → プロセス間通信 → 結果返却
4. ガーベージコレクション
   └─ PluginGc監視 → タイムアウト → stop()
```

### フローチャート

```mermaid
flowchart TD
    A[プラグインコマンド実行] --> B{プラグイン実行中?}
    B -->|Yes| C[インターフェース取得]
    B -->|No| D[プラグインspawn]
    D --> E[通信モード決定]
    E --> F{LocalSocket対応?}
    F -->|Yes| G[LocalSocket接続]
    F -->|No| H[stdio接続]
    G --> I[make_plugin_interface]
    H --> I
    I --> J[PluginGc開始]
    J --> C
    C --> K[コマンド実行要求送信]
    K --> L[結果受信]
    L --> M[結果返却]

    N[PluginGc] --> O{タイムアウト?}
    O -->|Yes| P{ロック中?}
    P -->|No| Q[プラグイン停止]
    P -->|Yes| R[待機継続]
    O -->|No| R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-90-01 | 通信プロトコル | MsgPack/JSONでシリアライズ | 常時 |
| BR-90-02 | 通信モード | stdio優先、LocalSocket対応時は切り替え | プラグイン起動時 |
| BR-90-03 | GC設定 | plugin_gc.enabled/stop_after設定に従う | GC有効時 |
| BR-90-04 | 永続化 | plugin.msgpackz形式で保存 | plugin add/rm時 |
| BR-90-05 | スレッド安全性 | Plugin/PluginCommandはSync必須 | 常時 |

### 計算ロジック

**GCタイムアウト計算**: `stop_after - (now - last_update)`

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

該当なし（ファイルベースの永続化）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| PluginFailedToLoad | プラグインエラー | プラグインバイナリが見つからない | パス確認 |
| NushellFailed | 内部エラー | Mutexポイズン | 再起動 |
| ServePluginError | プラグインエラー | プラグイン側でエラー発生 | エラーメッセージ表示 |
| IOError | 通信エラー | プロセス間通信失敗 | 再試行/再起動 |

### リトライ仕様

通信モード失敗時はstdioにフォールバック

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

該当なし

## パフォーマンス要件

- プラグイン起動は数百ミリ秒以内
- コマンド実行はオーバーヘッド最小限
- GCによる自動停止でリソース節約

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

- プラグインは信頼されたバイナリのみ登録
- プラグイン設定は$env.config.pluginsで管理

## 備考

- feature: pluginが必要
- LocalSocketはfeature: local-socketが必要
- プラグインテストにはnu-plugin-test-supportクレート使用

---

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

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

### 推奨読解順序

#### Step 1: プラグインAPIを理解する

プラグイン開発者向けのトレイト定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mod.rs | `crates/nu-plugin/src/plugin/mod.rs` | Pluginトレイト |
| 1-2 | command.rs | `crates/nu-plugin/src/plugin/command.rs` | PluginCommand/SimplePluginCommandトレイト |

**読解のコツ**:
- **mod.rs 99-258行目**: Pluginトレイト定義（version, commands, custom_value_*メソッド）
- **mod.rs 278-376行目**: serve_plugin関数（プラグインのメインエントリーポイント）
- **command.rs 75-176行目**: PluginCommandトレイト
- **command.rs 235-334行目**: SimplePluginCommandトレイト

#### Step 2: serve_plugin処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | mod.rs | `crates/nu-plugin/src/plugin/mod.rs` | serve_plugin_io関数 |

**主要処理フロー**:
1. **459-508行目**: serve_plugin_io関数の初期化
2. **510-642行目**: メインループ（ReceivedPluginCall処理）
3. **655-732行目**: custom_value_op関数

#### Step 3: エンジン側のプラグイン管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | persistent.rs | `crates/nu-plugin-engine/src/persistent.rs` | PersistentPlugin |

**主要処理フロー**:
- **23-28行目**: PersistentPlugin構造体
- **61-74行目**: new関数
- **80-133行目**: get関数（プラグイン取得/起動）
- **136-227行目**: spawn関数（プロセス起動）

#### Step 4: ガーベージコレクションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | gc.rs | `crates/nu-plugin-engine/src/gc.rs` | PluginGc |

**主要処理フロー**:
- **14-17行目**: PluginGc構造体
- **19-41行目**: new関数（GCスレッド起動）
- **110-165行目**: handle_message関数
- **167-212行目**: run関数（メインループ）

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

```
プラグインコマンド実行
    │
    ├─ PluginDeclaration::run()
    │      │
    │      └─ GetPlugin::get_plugin()
    │             │
    │             └─ PersistentPlugin::get()
    │                    │
    │                    └─ PersistentPlugin::spawn()
    │                           │
    │                           ├─ create_command()
    │                           │
    │                           ├─ make_plugin_interface()
    │                           │
    │                           └─ PluginGc::new()
    │
    └─ PluginInterface::run()
           │
           └─ [プロセス間通信]
                  │
                  └─ [プラグインプロセス]
                         │
                         └─ serve_plugin_io()
                                │
                                └─ PluginCommand::run()

プラグイン側 (serve_plugin)
    │
    ├─ tell_nushell_encoding()
    │
    ├─ serve_plugin_io()
    │      │
    │      ├─ EngineInterfaceManager::new()
    │      │
    │      ├─ interface.hello()
    │      │
    │      └─ call_receiver ループ
    │             │
    │             ├─ ReceivedPluginCall::Metadata
    │             ├─ ReceivedPluginCall::Signature
    │             ├─ ReceivedPluginCall::Run
    │             │      └─ command.run()
    │             ├─ ReceivedPluginCall::CustomValueOp
    │             │      └─ custom_value_op()
    │             └─ ReceivedPluginCall::GetCompletion
    │
    └─ print_help() [--help時]
```

### データフロー図

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

plugin add ───▶ spawn ───▶ Signature取得 ───▶ plugin.msgpackz
  バイナリ       起動        シグネチャ         永続化

コマンド実行 ───▶ PersistentPlugin ───▶ PluginInterface ───▶ 結果
  ls (plugin)    get/spawn            run               PipelineData

[エンジン]                [通信]              [プラグイン]
                         MsgPack
PluginInterface ───▶ stdin/LocalSocket ───▶ serve_plugin_io
      │                    ▲                       │
      │                    │                       │
      └──────────────◀─────┴───────────────────────┘
                        stdout
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mod.rs | `crates/nu-plugin/src/plugin/mod.rs` | ソース | Pluginトレイト、serve_plugin |
| command.rs | `crates/nu-plugin/src/plugin/command.rs` | ソース | PluginCommand/SimplePluginCommand |
| persistent.rs | `crates/nu-plugin-engine/src/persistent.rs` | ソース | PersistentPlugin |
| gc.rs | `crates/nu-plugin-engine/src/gc.rs` | ソース | PluginGc（ガーベージコレクタ） |
| declaration.rs | `crates/nu-plugin-engine/src/declaration.rs` | ソース | PluginDeclaration |
| init.rs | `crates/nu-plugin-engine/src/init.rs` | ソース | プラグイン初期化 |
| process.rs | `crates/nu-plugin-engine/src/process.rs` | ソース | プロセス管理 |
| interface/mod.rs | `crates/nu-plugin/src/plugin/interface/mod.rs` | ソース | EngineInterface |
| interface/mod.rs | `crates/nu-plugin-engine/src/interface/mod.rs` | ソース | PluginInterface |
| config_files.rs | `crates/nu-cli/src/config_files.rs` | ソース | プラグインファイル読み込み |
| plugin_gc.rs | `crates/nu-protocol/src/config/plugin_gc.rs` | ソース | PluginGcConfig定義 |
