# 機能設計書 81-JSON-RPC通信

## 概要

本ドキュメントは、lua-language-serverにおけるJSON-RPC通信機能の設計について記載する。この機能は、Language Server Protocol (LSP) の基盤となるJSON-RPCプロトコルのエンコード・デコード処理を担当する。

### 本機能の処理概要

**業務上の目的・背景**：Language Server Protocol (LSP) は、エディタとLanguage Server間の通信にJSON-RPCプロトコルを採用している。本機能は、このプロトコルに準拠したメッセージのエンコード（送信用）およびデコード（受信用）を提供することで、LSPサーバーとクライアント間の通信基盤を実現する。標準入出力またはソケットを介したバイナリデータストリームからJSONメッセージを正しく抽出し、Luaテーブルとして扱えるようにする。

**機能の利用シーン**：LSPクライアント（VSCodeなど）からのリクエスト受信時、およびサーバーからクライアントへのレスポンス・通知送信時に利用される。初期化ハンドシェイクから、コード補完、診断、定義ジャンプまで、すべてのLSP通信でこの機能が基盤として動作する。

**主要な処理内容**：
1. JSON-RPCメッセージのエンコード（Luaテーブル→ヘッダー付きJSONバイト列）
2. JSON-RPCメッセージのデコード（ヘッダー付きJSONバイト列→Luaテーブル）
3. Content-Lengthヘッダーの解析と生成
4. ストリームリーダーからのプロトコルヘッダー読み取り

**関連システム・外部連携**：標準入出力（stdio）またはUnixソケットを介してVSCodeなどのLSPクライアントと通信する。JSON処理にはjsonモジュール（script/json.lua）を使用する。

**権限による制御**：特になし。すべての通信で同一の処理が適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たず、バックエンド通信機能として動作 |

## 機能種別

データ連携 / 通信プロトコル処理

## 入力仕様

### 入力パラメータ

#### encode関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pack | table | Yes | 送信するJSONオブジェクト（id, method, params, result, error等を含む） | tableであること |

#### decode関数

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reader | function | Yes | バイト列を読み取る関数。引数に読み取りバイト数を受け取り、文字列を返す | 関数であること |

### 入力データソース

- encode: 上位モジュール（proto.lua）から渡されるLSPメッセージテーブル
- decode: 標準入力またはソケットから読み取られるバイトストリーム

## 出力仕様

### 出力データ

#### encode関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| buf | string | Content-Lengthヘッダー付きのJSON-RPCメッセージ文字列 |

#### decode関数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| res | table/nil | パースされたJSONオブジェクト、またはエラー時nil |
| err | string/nil | エラーメッセージ（成功時はnil） |

### 出力先

- encode: 標準出力またはソケット（proto.lua経由）
- decode: 上位モジュール（proto.lua）へLuaテーブルとして返却

## 処理フロー

### 処理シーケンス

```
encode処理:
1. jsonrpc フィールドを '2.0' に設定
   └─ LSPはJSON-RPC 2.0を使用
2. テーブルをJSON文字列にエンコード
   └─ json.encode()を使用
3. Content-Lengthヘッダーを生成
   └─ バイト長を計算してヘッダー文字列を作成
4. ヘッダーとボディを結合して返却
   └─ "Content-Length: {len}\r\n\r\n{json}"形式

decode処理:
1. プロトコルヘッダーを読み取り
   └─ readProtoHead()で行単位に読み取り
2. Content-Lengthを解析
   └─ ヘッダーからバイト長を取得
3. コンテンツボディを読み取り
   └─ 指定バイト長を読み取り
4. JSONをパースしてテーブルに変換
   └─ json.decode()でLuaテーブルに変換
```

### フローチャート

```mermaid
flowchart TD
    subgraph encode["encode処理"]
        E1[pack受け取り] --> E2["jsonrpc='2.0'設定"]
        E2 --> E3[json.encode実行]
        E3 --> E4[Content-Length計算]
        E4 --> E5["ヘッダー+ボディ結合"]
        E5 --> E6[文字列を返却]
    end

    subgraph decode["decode処理"]
        D1[reader関数受け取り] --> D2[readProtoHead呼び出し]
        D2 --> D3{ヘッダー解析成功?}
        D3 -->|No| D4[エラー返却]
        D3 -->|Yes| D5[Content-Length取得]
        D5 --> D6{長さ取得成功?}
        D6 -->|No| D7[エラー返却]
        D6 -->|Yes| D8[ボディ読み取り]
        D8 --> D9{読み取り成功?}
        D9 -->|No| D10[エラー返却]
        D9 -->|Yes| D11[json.decode実行]
        D11 --> D12{パース成功?}
        D12 -->|No| D13[エラー返却]
        D12 -->|Yes| D14[テーブル返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-81-01 | JSON-RPCバージョン | エンコード時にjsonrpcフィールドを'2.0'に設定する | 全てのエンコード処理 |
| BR-81-02 | ヘッダー形式 | Content-Length: {n}\r\n\r\n の形式でヘッダーを生成する | 全てのエンコード処理 |
| BR-81-03 | null処理 | デコード時にjson.nullをnilに置換する | 全てのデコード処理 |
| BR-81-04 | 切断検知 | reader関数がnilを返した場合、接続が切断されたと判断する | デコード処理 |

### 計算ロジック

特になし

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Disconnected! | reader関数がnilを返した | 接続終了処理を実行 |
| - | Proto header error | ヘッダー形式が不正 | エラーを上位に通知 |
| - | Proto read error | ボディ読み取り失敗 | エラーを上位に通知 |
| - | Proto parse error | JSON解析失敗 | エラーを上位に通知 |

### リトライ仕様

リトライは行わない。エラー発生時は上位モジュールに通知し、処理を中断する。

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

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

## パフォーマンス要件

- エンコード・デコードは高頻度で呼び出されるため、オーバーヘッドを最小限に抑える
- メモリアロケーションを最小化し、GC負荷を軽減する

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

- 信頼されたクライアント（エディタ）からのみ接続を受け付ける前提
- 入力バリデーションはJSON.decodeに委ねる

## 備考

本機能は、proto.lua（プロトコル管理モジュール）から呼び出され、実際の通信チャネル（stdio/socket）との橋渡し役を担う。

---

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

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

### 推奨読解順序

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

JSON-RPCメッセージの構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | jsonrpc.lua | `script/jsonrpc.lua` | モジュール定義とJSON-RPCメッセージ構造 |

**読解のコツ**: JSON-RPC 2.0仕様を理解しておくと、なぜjsonrpcフィールドを'2.0'に設定するか理解できる。

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

主要な関数はencode()とdecode()の2つである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | jsonrpc.lua | `script/jsonrpc.lua` | encode関数（10-15行目）、decode関数（49-71行目） |

**主要処理フロー**:
1. **10-15行目**: encode関数 - packテーブルをJSON-RPC形式の文字列に変換
2. **11行目**: jsonrpcバージョンを'2.0'に設定
3. **12行目**: json.encodeでJSONシリアライズ
4. **13行目**: Content-Lengthヘッダーを付与

#### Step 3: ヘッダー解析処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | jsonrpc.lua | `script/jsonrpc.lua` | readProtoHead関数（18-46行目） |

**主要処理フロー**:
- **21-44行目**: ヘッダー行を1文字ずつ読み取り、\r\nで区切って解析
- **34行目**: キー:値形式でヘッダーをパース
- **38-39行目**: Content-Lengthは数値に変換

#### Step 4: デコード処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | jsonrpc.lua | `script/jsonrpc.lua` | decode関数（49-71行目） |

**主要処理フロー**:
- **50-53行目**: ヘッダー解析の呼び出しとエラーチェック
- **54-57行目**: Content-Length取得
- **58-61行目**: ボディ読み取り
- **63-66行目**: json.nullの置換処理とJSONパース

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

```
proto.send() / pub.task()
    │
    ├─ jsonrpc.encode(pack)
    │      └─ json.encode(content)
    │
    └─ jsonrpc.decode(reader)
           ├─ readProtoHead(reader)
           │      └─ reader(1)  [1バイトずつ読み取り]
           ├─ reader(len)  [ボディ読み取り]
           └─ json.decode(content)
```

### データフロー図

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

encode:
Luaテーブル ───▶ json.encode ───▶ ヘッダー付与 ───▶ バイト文字列
                                                  (Content-Length: n\r\n\r\n{json})

decode:
バイトストリーム ───▶ readProtoHead ───▶ json.decode ───▶ Luaテーブル
    (reader関数)      (ヘッダー解析)     (JSONパース)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| jsonrpc.lua | `script/jsonrpc.lua` | ソース | JSON-RPCエンコード・デコードの実装 |
| json.lua | `script/json.lua` | ソース | JSONシリアライズ・デシリアライズ |
| proto.lua | `script/proto/proto.lua` | ソース | JSON-RPCを使用する上位モジュール |
| inspect.lua | `(外部モジュール)` | ソース | デバッグ用のテーブル文字列化 |
