Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .cursor/rules/rust-coding.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
description: Rustコーディングルール(型・Result・所有権・clippy)
alwaysApply: false
globs: **/*.rs,**/Cargo.toml
---

# Rust コーディングルール

既存の Cargo ワークスペース・クレート構成に従うこと。本ルールは新規実装・リファクタ時の共通方針とする。

## プロジェクト構造

- `Cargo.toml` / `Cargo.lock` の既存パターン(workspace、features、dependencies)を崩さない。
- バイナリとライブラリを分ける場合は、再利用ロジックは `lib` に置き、`main.rs` は薄く保つ。
- 1モジュールが肥大化したら、責務単位で `mod` 分割し、`mod.rs` または `foo.rs` + `foo/` ディレクトリに整理する。

### 責務の分離

| 層 | 責務 |
|---|---|
| `domain` / `model` | 型・不変条件・純粋な変換 |
| `service` / `use_case` | ビジネスロジックの組み立て |
| `infra` / `adapter` | DB・HTTP・ファイル I/O など外部境界 |
| `api` / `cli` | 入出力の変換・ルーティング |

下位層(domain)が上位層(api)に依存しないこと。循環依存は禁止。

---

## エラー処理

- ライブラリクレートでは `Result<T, E>` を返し、**本番コードで `unwrap()` / `expect()` を使わない**(プロトタイプ・テスト・`main` の起動失敗メッセージ等、既存慣習がある場合は除く)。
- エラー型は `thiserror` またはプロジェクト既存の方式に合わせる。アプリケーション境界では `anyhow` 可(既採用時のみ)。
- `panic!` は回復不能なバグ用に限定し、入力検証失敗は `Result` で表現する。
- `?` を優先し、不要な `match` のネストを避ける。

---

## 型指定(必須)

**新規・変更する Rust コードは、型で意図を表現する。** コンパイラ推論に任せきりにせず、公開 API とドメイン境界は明示的な型にする。

### 付与・明示の範囲

| 対象 | 要件 |
|------|------|
| `pub fn` / `pub struct` / `pub enum` | シグネチャ・フィールド型を明示。`///` ドキュメントを付ける |
| 関数の引数・戻り値 | 推論任せにせず、公開・再export される API は型を読み取れるように書く |
| エラー | 成功は `T`、失敗は `E`。`Result<T, E>` を返し、意味のあるエラー型を定義する |
| オプション | 「無いかもしれない」は `Option<T>`。「失敗しうる」は `Result<T, E>`。混同しない |
| 定数 | マジックナンバーは `const` / `enum` / `newtype` で名付ける |
| 内部コード | 推論で十分なら省略可。ただし複雑な式・ジェネリクス・`collect()` 前後は型を明示 |

### 設計の書き方

- **不正な状態を型で表現**する。フラグの組み合わせより `enum`、ID の混同防止に `struct UserId(u64)` 等の newtype。
- **所有権を型で表現**する。借用で足りる引数は `&str` / `&[T]`。所有権が必要なときだけ `String` / `Vec<T>`。
- 列挙で分岐が閉じるようにし、`match` は網羅的に(`#[must_use]` を適宜付与)。
- コレクション変換は型をはっきりさせる。例: `let items: Vec<User> = iter.collect()`(空の `collect()` 任せを避ける)。
- トレイト境界は必要最小限。公開 API が複雑になる場合は `impl Trait` や具体的な型を優先し、過剰な `<T: Foo + Bar + Baz>` を避ける。
- 動的ディスパッチが必要なときだけ `dyn Trait`。可能なら `enum` またはジェネリクスで静的に解決する。
- 非同期は `async fn foo() -> Result<Bar, Error>` のように戻り値を明示。`Future` を無名で返すだけにしない。

### 禁止・抑制

- 本番のライブラリコードで `unwrap()` / `expect()` に頼って型・エラーをごまかさない(テスト・`main` の起動処理は除く)。
- `panic!` で入力検証を済ませない。検証失敗は `Result` / `Option`。
- `todo!()` / `unimplemented!()` を本番パスに残さない。
- `as` キャストは理由がない限り使わない。`TryFrom` / `From` を検討する。
- `clone()` で所有権問題を回避する前に、借用・ライフタイム・データ構造の見直しをする。

### 検証

- 変更後は `cargo check` を必ず実行する。
- プロジェクトで `cargo clippy` / `cargo clippy -- -D warnings` がある場合は警告を解消する。
- `cargo test` で型・API 変更に伴うテスト破壊がないことを確認する。

### 例

```rust
// ❌ エラーを unwrap で握りつぶす
pub fn load_config(path: &str) -> Settings {
let data = std::fs::read_to_string(path).unwrap();
toml::from_str(&data).unwrap()
}

// ✅ 型で失敗を表現
pub fn load_config(path: &str) -> Result<Settings, ConfigError> {
let data = std::fs::read_to_string(path)?;
let settings: Settings = toml::from_str(&data)?;
Ok(settings)
}

// ❌ プリミティブの積み上げ
fn set_status(user_id: u64, status: u8) { ... }

// ✅ 意味のある型
struct UserId(u64);
enum UserStatus { Active, Suspended }

fn set_status(user_id: UserId, status: UserStatus) { ... }
```

### 公開 API ドキュメント

- `pub` 項目には `///` を付け、**エラー条件**(`Result::Err` の意味)、**パニック条件**(パニックする場合のみ)、**不変条件**を書く。
- 型パラメータ・ライフタイムが非自明なら、使用例を1行でもよいので doc コメントに含める。

---

## 実装スタイル

- `rustfmt` のフォーマットに従う。変更後は `cargo fmt` を実行する。
- `clippy` の警告を無視せず、既存の `#[allow(...)]` ポリシーに合わせる。
- `unsafe` は既存コードに必要な場合のみ。新規では避け、使う場合は安全性の根拠をコメントする。
- 非同期(`async`)はプロジェクトで既に採用されているランタイム(tokio 等)に合わせる。ブロッキング I/O を async 内に混ぜない。
- ログは `tracing` / `log` 等、プロジェクト既存のクレートを使う。`println!` デバッグ残しは避ける。

---

## 依存関係・設定

- 新規 crate 追加は理由を明示し、既存クレートで代替できないか確認する。
- 機能フラグ(`[features]`)でオプション機能を切り離す(既存パターンがある場合)。
- 環境依存の値(URL、しきい値、パス)はソースに直書きせず、設定ファイルまたは環境変数で読み込む。

---

## テストとの関係

- テストの書き方は `rules/testing/` および `rust-testing` ルールに従う。
- テスト内の `unwrap()` は可。本番コードの設計をテスト都合で劣化させない。

## 変更時の確認

- `cargo check` / `cargo build` / `cargo test` を実行し、失敗がないことを確認する。
- 公開 API(型・トレイト・エラー型)を変えた場合は、呼び出し元・テストをあわせて更新する。
- 既存の期待動作を壊さないこと。挙動変更は意図をコメントまたは changelog に残す。
107 changes: 107 additions & 0 deletions .cursor/rules/rust-testing.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
description: テスト作成ルール・Rust(等価分割・境界値・Given-When-Then)
alwaysApply: false
globs: **/*.rs,**/tests/**
---

# テスト作成ルール(共通)

テストコードの新規作成・追加・修正時は、言語固有ルールとあわせて本セクションを適用する。

## 1. 事前確認

- テストが必要かどうか不明な場合は、**AskUserQuestion** で確認すること。
- 着手前に以下を把握すること:
- 言語・ランタイム
- テストフレームワーク(プロジェクト既存のものを優先)
- 既存のテストディレクトリ・命名規則・フィクスチャの置き場所

## 2. 必須プロセス

1. **テスト観点の表**(等価分割・境界値)を Markdown 表で**先に**提示する。
2. 表に基づいてテストコードを実装する。
3. **失敗系を正常系と同数以上**含める。
4. 以下を必ず網羅する:
- **正常系** — 主要シナリオ・代表入力
- **異常系** — バリデーション失敗・エラー返却・例外
- **境界値** — 0、最小、最大、±1、空、NULL / `None`、空コレクション
- **不正入力** — 型・形式・範囲の不正さ
- **外部依存の失敗** — 該当する場合(モック・フェイクの前提を明示)
- **エラー内容の検証** — 例外種別・エラーメッセージ・エラーコード
5. 各テストケースに **Given / When / Then** 形式のコメントを付ける。
6. 回答末尾に **実行コマンド** と **カバレッジ取得方法** を記載する。
7. **目標: 分岐網羅 100%**(不足観点は実装前に表へ追記する)。

## 3. 変更時の必須確認

- コード修正を行ったら、**必ず**テストを実行して結果を確認する。
- 既存テストが落ちないこと。仕様変更はテストとドキュメントを同時に更新する。
- フレークのないテストにする(時刻・乱数・並列は固定シード・モックで制御)。


---

# テスト作成ルール(Rust)

上記「テスト作成ルール(共通)」に加え、Rust プロジェクトでは以下を適用する。

## 4. テストの種類と配置

| 種類 | 置き場所 | 用途 |
|---|---|---|
| ユニットテスト | 対象 `.rs` 内の `#[cfg(test)] mod tests` | 純粋関数・小さなモジュール |
| 統合テスト | クレート直下 `tests/*.rs` | 公開 API・複数モジュールの連携 |
| ドキュメントテスト | `///` コードブロック | 使用例・仕様の固定 |

- 新規統合テストは `tests/` に **1 ファイル 1 テーマ**で追加する(`tests/foo.rs`)。
- テスト専用ヘルパーは `tests/common/mod.rs` または `#[cfg(test)]` に置く。
- 既存の `proptest` 等があれば、その構成を優先する。

## 5. 実装の書き方

- アサーションは `assert!` / `assert_eq!` / `assert_matches!` を使い、失敗時に期待値が分かるメッセージを付ける。
- エラー系は `Result::Err`・`Option::None` を明示的に検証する。
- パニック期待は `#[should_panic]` または `catch_unwind` を使い、理由をコメントする。
- 非同期は `#[tokio::test]` 等、プロジェクト既存のマクロに合わせる。
- `unwrap()` は **テストコード内のみ** 許容。本番コードに `unwrap` を増やしてテストしやすくしない。

### 例

```rust
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parses_valid_hex() {
// Given: 32文字の16進文字列
// When: parse_hex_input を呼ぶ
// Then: 16バイトの Vec が返る
let got = parse_hex_input("00112233445566778899aabbccddeeff").unwrap();
assert_eq!(got.len(), 16);
}
}
```

## 6. 実行コマンドとカバレッジ

回答末尾に以下を含めること。

```bash
# クレート全体
cargo test

# 特定テストのみ
cargo test parses_valid_hex

# 統合テストのみ
cargo test --test integration_name
```

カバレッジ(`cargo-llvm-cov` 等がプロジェクトにあれば):

```bash
cargo llvm-cov --html
```

ツール未導入時は、プロジェクトで採用している手段を確認してから記載する。
9 changes: 3 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
target
.DS_Store

# Cursor / エージェント(ローカルのみ)
.cursor/*
!.cursor/rules/
.cursor/rules/*
!.cursor/rules/*.mdc
AGENTS.md
# Cursor: ルール・エージェント設定はリポジトリで共有(.cursor/rules/, AGENTS.md)
# ローカル専用のプラン・キャッシュのみ除外
.cursor/plans/
105 changes: 105 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

# Agent Configuration

## 基本方針

- 回答・説明・進捗報告は必ず日本語で行うこと。
- 実装・設計・調査は必ず Task ツールを使って進めること。
- subagent は常に親モデルを継承すること。
- Task ツール呼び出し時に `model` パラメータを指定してはならない。
- 不明点がある場合は、推測で進めず AskUserQuestion を使って質問すること。
- 既存の期待動作を壊さないことを最優先すること。

## Plan モード時のルール

Plan モードでは、実装前に必ず以下を明示すること。

- 作業目的
- 影響範囲
- 使用する subagent
- 各 subagent に担当させる内容
- 修正予定ファイル
- 確認・テスト方針
- 不明点がある場合は AskUserQuestion で確認すること

設計作業は必ず subagent に Task として依頼すること。

## 開発時のルール

開発作業は必ず subagent に Task として依頼すること。

Task 作成時は以下を明確にすること。

- 目的
- 対象ファイル
- 変更方針
- 期待される挙動
- 既存挙動への影響確認
- 実施すべきテストまたは確認方法

subagent を使う際、`model` パラメータは指定しないこと。

## 実装方針

実装は、目的を満たすために必要な最小限の機能に留めること。

- 要求されていない機能を追加しないこと。
- 将来使うかもしれないという理由だけで実装を増やさないこと。
- 既存の設計や実装方針に沿って、変更範囲を最小限にすること。
- 過剰な抽象化、過剰な汎用化、不要なリファクタリングを避けること。
- ただし、可読性・保守性・テスト容易性が向上する場合は適切に関数化すること。

関数化する際は、以下を意識すること。

- 1つの関数は1つの責務に集中させること。
- 複雑な条件分岐や重複処理は関数として切り出すこと。
- 関数名から処理内容が分かるようにすること。
- 呼び出し元の可読性が下がるような過度な分割は避けること。
- 既存の関数やユーティリティで代用できる場合は新規作成しないこと。

## 設定ファイル

環境依存の値・しきい値・URL・機能フラグなどは、ソースコードに直接書かず **別ファイルの設定** として管理すること。

- 定数や設定値をプログラム内にハードコードしないこと。
- プロジェクトの慣習に合わせ、設定用ファイル(例: `.env`、`config.yaml`、`settings.toml`、フレームワークの設定ディレクトリ)を作成し、そこにまとめること。
- 既存の設定ファイルや読み込み処理がある場合は、それを拡張すること。同目的の設定ファイルを重複して作らないこと。
- シークレット(API キー、トークン、パスワード)はリポジトリにコミットせず、環境変数やローカル専用ファイル(`.env.local` 等)で扱うこと。
- 新規に設定ファイルを追加する場合は、配置場所・読み込み方法・必須項目をコード変更とあわせて明示すること。

## Web 検索のルール

以下の場合は web 検索を行うこと。

- 技術的に詰まった場合
- 原因不明のエラーが解決できない場合
- 使用しているライブラリ、API、フレームワークの仕様に不確実性がある場合
- 最新の仕様や既知の不具合を確認する必要がある場合

検索時は、可能な限り以下を優先すること。

- 公式ドキュメント
- 公式リポジトリ
- Issue / Discussion
- 信頼できる技術記事

検索結果を利用した場合は、何を根拠に判断したかを簡潔に説明すること。

## 修正前の確認

ファイルを修正する前に、必ず以下を提示すること。

- 現在のディレクトリ構造
- 修正対象ファイル
- 各ファイルを修正する理由

例:

```text
.
├── src/
│ ├── components/
│ │ └── Button.tsx # 表示ロジックの修正対象
│ └── utils/
│ └── format.ts # 既存処理への影響確認対象
└── package.json # テストコマンド確認対象
Loading