mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-18 06:43:05 +08:00
docs: add missing Japanese translations to complete zh-CN parity (ja-JP)
Add remaining files to match zh-CN documentation structure: - hooks/README.md — hooks architecture and customization guide - examples/ — 8 project CLAUDE.md templates (general, user, django, go, harmonyos, laravel, rust, saas-nextjs) - CHANGELOG.md — version history - the-openclaw-guide.md — OpenClaw guide (471 lines) Total: 11 files, 2362 insertions ja-JP now has full parity with zh-CN directory structure.
This commit is contained in:
285
docs/ja-JP/examples/rust-api-CLAUDE.md
Normal file
285
docs/ja-JP/examples/rust-api-CLAUDE.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# Rust API サービス — プロジェクト CLAUDE.md
|
||||
|
||||
> Axum、PostgreSQL、Dockerを使用したRust APIサービスの実世界サンプル。
|
||||
> これをプロジェクトのルートにコピーしてサービスに合わせてカスタマイズしてください。
|
||||
|
||||
## プロジェクト概要
|
||||
|
||||
**スタック:** Rust 1.78+, Axum(Webフレームワーク), SQLx(非同期データベース), PostgreSQL, Tokio(非同期ランタイム), Docker
|
||||
|
||||
**アーキテクチャ:** ハンドラー → サービス → リポジトリの分離を持つレイヤードアーキテクチャ。HTTPにAxum、コンパイル時に型チェックされたSQLにSQLx、横断的関心事にTowerミドルウェアを使用。
|
||||
|
||||
## 重要なルール
|
||||
|
||||
### Rust の規約
|
||||
|
||||
- ライブラリエラーには `thiserror` を使用し、`anyhow` はバイナリクレートまたはテストのみ
|
||||
- 本番コードで `.unwrap()` や `.expect()` を使用しない — `?` でエラーを伝播させる
|
||||
- 関数パラメーターでは `String` より `&str` を優先し、所有権が移転するときは `String` を返す
|
||||
- `#![deny(clippy::all, clippy::pedantic)]` で `clippy` を使用 — すべての警告を修正する
|
||||
- すべての公開型に `Debug` を導出し、`Clone`、`PartialEq` は必要な場合のみ導出する
|
||||
- `// SAFETY:` コメントによる正当化がない限り `unsafe` ブロックは使用しない
|
||||
|
||||
### データベース
|
||||
|
||||
- すべてのクエリはSQLxの `query!` または `query_as!` マクロを使用 — スキーマに対してコンパイル時に検証される
|
||||
- `migrations/` のマイグレーションは `sqlx migrate` を使用 — データベースを直接変更しない
|
||||
- 共有状態として `sqlx::Pool<Postgres>` を使用 — リクエストごとにコネクションを作成しない
|
||||
- すべてのクエリはパラメータ化プレースホルダー(`$1`, `$2`)を使用 — 文字列フォーマットは禁止
|
||||
|
||||
```rust
|
||||
// 悪い例: 文字列補間(SQLインジェクションリスク)
|
||||
let q = format!("SELECT * FROM users WHERE id = '{}'", id);
|
||||
|
||||
// 良い例: パラメータ化クエリ、コンパイル時チェック済み
|
||||
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
|
||||
.fetch_optional(&pool)
|
||||
.await?;
|
||||
```
|
||||
|
||||
### エラーハンドリング
|
||||
|
||||
- `thiserror` でモジュールごとにドメインエラーenumを定義する
|
||||
- `IntoResponse` でエラーをHTTPレスポンスにマップ — 内部詳細を公開しない
|
||||
- 構造化ロギングには `tracing` を使用 — `println!` や `eprintln!` は使用しない
|
||||
|
||||
```rust
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AppError {
|
||||
#[error("Resource not found")]
|
||||
NotFound,
|
||||
#[error("Validation failed: {0}")]
|
||||
Validation(String),
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized,
|
||||
#[error(transparent)]
|
||||
Internal(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, message) = match &self {
|
||||
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
|
||||
Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
|
||||
Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
|
||||
Self::Internal(err) => {
|
||||
tracing::error!(?err, "internal error");
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
|
||||
}
|
||||
};
|
||||
(status, Json(json!({ "error": message }))).into_response()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### テスト
|
||||
|
||||
- 各ソースファイル内の `#[cfg(test)]` モジュールにユニットテストを記述する
|
||||
- `tests/` ディレクトリに実際のPostgreSQL(TestcontainersまたはDocker)を使用した統合テストを記述する
|
||||
- 自動マイグレーションとロールバック付きのデータベーステストには `#[sqlx::test]` を使用する
|
||||
- 外部サービスのモックには `mockall` または `wiremock` を使用する
|
||||
|
||||
### コードスタイル
|
||||
|
||||
- 最大行長: 100文字(rustfmtにより強制)
|
||||
- インポートのグループ化: `std`、外部クレート、`crate`/`super` — 空白行で区切る
|
||||
- モジュール: モジュールごとに1ファイル、`mod.rs` は再エクスポートのみ
|
||||
- 型: PascalCase、関数/変数: snake_case、定数: UPPER_SNAKE_CASE
|
||||
|
||||
## ファイル構成
|
||||
|
||||
```
|
||||
src/
|
||||
main.rs # エントリーポイント、サーバーセットアップ、グレースフルシャットダウン
|
||||
lib.rs # 統合テスト用の再エクスポート
|
||||
config.rs # envyまたはfigmentによる環境設定
|
||||
router.rs # すべてのルートを持つAxumルーター
|
||||
middleware/
|
||||
auth.rs # JWT抽出とバリデーション
|
||||
logging.rs # リクエスト/レスポンスのトレーシング
|
||||
handlers/
|
||||
mod.rs # ルートハンドラー(薄く — サービスに委任)
|
||||
users.rs
|
||||
orders.rs
|
||||
services/
|
||||
mod.rs # ビジネスロジック
|
||||
users.rs
|
||||
orders.rs
|
||||
repositories/
|
||||
mod.rs # データベースアクセス(SQLxクエリ)
|
||||
users.rs
|
||||
orders.rs
|
||||
domain/
|
||||
mod.rs # ドメイン型、エラーenum
|
||||
user.rs
|
||||
order.rs
|
||||
migrations/
|
||||
001_create_users.sql
|
||||
002_create_orders.sql
|
||||
tests/
|
||||
common/mod.rs # 共有テストヘルパー、テストサーバーセットアップ
|
||||
api_users.rs # ユーザーエンドポイントの統合テスト
|
||||
api_orders.rs # 注文エンドポイントの統合テスト
|
||||
```
|
||||
|
||||
## 主要なパターン
|
||||
|
||||
### ハンドラー(薄く)
|
||||
|
||||
```rust
|
||||
async fn create_user(
|
||||
State(ctx): State<AppState>,
|
||||
Json(payload): Json<CreateUserRequest>,
|
||||
) -> Result<(StatusCode, Json<UserResponse>), AppError> {
|
||||
let user = ctx.user_service.create(payload).await?;
|
||||
Ok((StatusCode::CREATED, Json(UserResponse::from(user))))
|
||||
}
|
||||
```
|
||||
|
||||
### サービス(ビジネスロジック)
|
||||
|
||||
```rust
|
||||
impl UserService {
|
||||
pub async fn create(&self, req: CreateUserRequest) -> Result<User, AppError> {
|
||||
if self.repo.find_by_email(&req.email).await?.is_some() {
|
||||
return Err(AppError::Validation("Email already registered".into()));
|
||||
}
|
||||
|
||||
let password_hash = hash_password(&req.password)?;
|
||||
let user = self.repo.insert(&req.email, &req.name, &password_hash).await?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### リポジトリ(データアクセス)
|
||||
|
||||
```rust
|
||||
impl UserRepository {
|
||||
pub async fn find_by_email(&self, email: &str) -> Result<Option<User>, sqlx::Error> {
|
||||
sqlx::query_as!(User, "SELECT * FROM users WHERE email = $1", email)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn insert(
|
||||
&self,
|
||||
email: &str,
|
||||
name: &str,
|
||||
password_hash: &str,
|
||||
) -> Result<User, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
User,
|
||||
r#"INSERT INTO users (email, name, password_hash)
|
||||
VALUES ($1, $2, $3) RETURNING *"#,
|
||||
email, name, password_hash,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 統合テスト
|
||||
|
||||
```rust
|
||||
#[tokio::test]
|
||||
async fn test_create_user() {
|
||||
let app = spawn_test_app().await;
|
||||
|
||||
let response = app
|
||||
.client
|
||||
.post(&format!("{}/api/v1/users", app.address))
|
||||
.json(&json!({
|
||||
"email": "alice@example.com",
|
||||
"name": "Alice",
|
||||
"password": "securepassword123"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::CREATED);
|
||||
let body: serde_json::Value = response.json().await.unwrap();
|
||||
assert_eq!(body["email"], "alice@example.com");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user_duplicate_email() {
|
||||
let app = spawn_test_app().await;
|
||||
// 最初のユーザーを作成
|
||||
create_test_user(&app, "alice@example.com").await;
|
||||
// 重複を試みる
|
||||
let response = create_user_request(&app, "alice@example.com").await;
|
||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
```
|
||||
|
||||
## 環境変数
|
||||
|
||||
```bash
|
||||
# サーバー
|
||||
HOST=0.0.0.0
|
||||
PORT=8080
|
||||
RUST_LOG=info,tower_http=debug
|
||||
|
||||
# データベース
|
||||
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
|
||||
|
||||
# 認証
|
||||
JWT_SECRET=your-secret-key-min-32-chars
|
||||
JWT_EXPIRY_HOURS=24
|
||||
|
||||
# 任意
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:3000
|
||||
```
|
||||
|
||||
## テスト戦略
|
||||
|
||||
```bash
|
||||
# すべてのテストを実行
|
||||
cargo test
|
||||
|
||||
# 出力付きで実行
|
||||
cargo test -- --nocapture
|
||||
|
||||
# 特定のテストモジュールを実行
|
||||
cargo test api_users
|
||||
|
||||
# カバレッジチェック(cargo-llvm-covが必要)
|
||||
cargo llvm-cov --html
|
||||
open target/llvm-cov/html/index.html
|
||||
|
||||
# リント
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
# フォーマットチェック
|
||||
cargo fmt -- --check
|
||||
```
|
||||
|
||||
## ECCワークフロー
|
||||
|
||||
```bash
|
||||
# 計画
|
||||
/plan "Add order fulfillment with Stripe payment"
|
||||
|
||||
# TDDによる開発
|
||||
/tdd # cargo test ベースのTDDワークフロー
|
||||
|
||||
# レビュー
|
||||
/code-review # Rust固有のコードレビュー
|
||||
/security-scan # 依存関係監査 + unsafeスキャン
|
||||
|
||||
# 検証
|
||||
/verify # ビルド、clippy、テスト、セキュリティスキャン
|
||||
```
|
||||
|
||||
## Git ワークフロー
|
||||
|
||||
- `feat:` 新機能、`fix:` バグ修正、`refactor:` コード変更
|
||||
- `main` からフィーチャーブランチを切り、PRが必要
|
||||
- CI: `cargo fmt --check`, `cargo clippy`, `cargo test`, `cargo audit`
|
||||
- デプロイ: `scratch` または `distroless` ベースのDockerマルチステージビルド
|
||||
Reference in New Issue
Block a user