# HttpClient API リファレンス

## 概要

`HttpClient`は、AT Protocol APIへの統一されたHTTPクライアントインターフェースです。認証、エラーハンドリング、リクエスト管理を自動化します。

## 基本的な使用方法

```rust
use crate::http_client::HttpClient;

let client = HttpClient::new();
// または
let client = HttpClient::default();
```

## APIメソッド

### 認証付きリクエスト

#### get_with_auth
AT Protocolの認証が必要なGETリクエストを実行します。

```rust
pub async fn get_with_auth(&self, url: &str) -> Result<String, Error>
```

**パラメータ:**
- `url`: リクエスト先のURL

**戻り値:**
- `Ok(String)`: レスポンスボディ（文字列）
- `Err(Error)`: リクエストエラー

**使用例:**
```rust
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/app.bsky.actor.getProfile?actor=user.bsky.social";

match client.get_with_auth(&url).await {
    Ok(response) => println!("Response: {}", response),
    Err(e) => eprintln!("Error: {}", e),
}
```

#### post_json_with_auth
AT Protocolの認証が必要なPOSTリクエスト（JSON）を実行します。

```rust
pub async fn post_json_with_auth<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
```

**パラメータ:**
- `url`: リクエスト先のURL
- `json`: シリアライズ可能なJSONデータ

**戻り値:**
- `Ok(String)`: レスポンスボディ（文字列）
- `Err(Error)`: リクエストエラー

**使用例:**
```rust
use serde_json::json;

let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
let payload = json!({
    "repo": "user.bsky.social",
    "collection": "app.bsky.feed.post",
    "record": {
        "text": "Hello, World!",
        "createdAt": "2025-01-01T00:00:00Z"
    }
});

match client.post_json_with_auth(&url, &payload).await {
    Ok(response) => println!("Post created: {}", response),
    Err(e) => eprintln!("Error: {}", e),
}
```

#### delete_with_auth
AT Protocolの認証が必要なDELETEリクエストを実行します。

```rust
pub async fn delete_with_auth(&self, url: &str) -> Result<String, Error>
```

**パラメータ:**
- `url`: リクエスト先のURL

**戻り値:**
- `Ok(String)`: レスポンスボディ（文字列）
- `Err(Error)`: リクエストエラー

**使用例:**
```rust
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.repo.deleteRecord";

match client.delete_with_auth(&url).await {
    Ok(response) => println!("Deleted: {}", response),
    Err(e) => eprintln!("Error: {}", e),
}
```

### 認証なしリクエスト

#### get
認証なしのGETリクエストを実行します。

```rust
pub async fn get(&self, url: &str) -> Result<String, Error>
```

**使用例:**
```rust
let client = HttpClient::new();
let url = "https://public-api.example.com/data";

match client.get(&url).await {
    Ok(response) => println!("Response: {}", response),
    Err(e) => eprintln!("Error: {}", e),
}
```

#### post_json
認証なしのPOSTリクエスト（JSON）を実行します。ログイン処理などで使用。

```rust
pub async fn post_json<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
```

**使用例:**
```rust
use serde_json::json;

let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.session.create";
let credentials = json!({
    "identifier": "user.bsky.social",
    "password": "password"
});

match client.post_json(&url, &credentials).await {
    Ok(response) => println!("Login successful: {}", response),
    Err(e) => eprintln!("Login failed: {}", e),
}
```

### カスタムヘッダー付きリクエスト

#### post_with_headers
カスタムヘッダーを指定したPOSTリクエストを実行します。

```rust
pub async fn post_with_headers<T: Serialize>(
    &self, 
    url: &str, 
    json: &T,
    headers: Vec<(&str, &str)>
) -> Result<String, Error>
```

**パラメータ:**
- `url`: リクエスト先のURL
- `json`: シリアライズ可能なJSONデータ
- `headers`: ヘッダーのキーと値のペアのベクター

**使用例:**
```rust
use serde_json::json;

let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.session.refresh";
let refresh_token = "refresh_token_value";
let auth_header = format!("Bearer {}", refresh_token);
let headers = vec![("Authorization", auth_header.as_str())];
let empty_json = json!({});

match client.post_with_headers(&url, &empty_json, headers).await {
    Ok(response) => println!("Token refreshed: {}", response),
    Err(e) => eprintln!("Refresh failed: {}", e),
}
```

## 内部実装詳細

### 認証処理
認証付きメソッドは自動的に以下を実行します：

1. `data_refresh(&"access")`でアクセストークンを取得
2. `Authorization: Bearer {token}`ヘッダーを自動追加
3. リクエストを実行

### エラーハンドリング
- ネットワークエラー
- HTTPステータスエラー
- JSON解析エラー
- タイムアウトエラー

すべて`reqwest::Error`として統一されて返されます。

## 使用上の注意

### 1. トークン管理
- アクセストークンは自動的に取得されます
- トークンの有効期限切れは呼び出し元で処理する必要があります
- リフレッシュトークンは`post_with_headers`で手動設定

### 2. エラー処理パターン
```rust
// 推奨パターン
match client.get_with_auth(&url).await {
    Ok(response) => {
        // 成功処理
        response
    },
    Err(e) => {
        eprintln!("API call failed: {}", e);
        "err".to_string() // 既存コードとの互換性
    }
}
```

### 3. パフォーマンス
- `HttpClient`の作成は軽量な操作です
- 内部でrequwestクライアントを再利用しています
- 複数のリクエストで同じインスタンスを使い回すことも可能

### 4. デバッグ
リクエスト/レスポンスの詳細をログ出力したい場合：
```rust
// HttpClientモジュール内で適宜printlnを追加
println!("Request URL: {}", url);
println!("Response: {}", response);
```

## 実装例：新しいAPI呼び出しモジュール

```rust
use crate::http_client::HttpClient;
use crate::{data_toml, url};
use serde_json::json;
use iso8601_timestamp::Timestamp;

pub async fn create_custom_record(data: String) -> String {
    let did = data_toml(&"did");
    let handle = data_toml(&"handle");
    let url = url(&"record_create");
    
    let timestamp = Timestamp::now_utc().to_string();
    
    let payload = json!({
        "repo": handle,
        "did": did,
        "collection": "app.bsky.custom.record",
        "record": {
            "data": data,
            "createdAt": timestamp
        }
    });
    
    let client = HttpClient::new();
    match client.post_json_with_auth(&url, &payload).await {
        Ok(response) => response,
        Err(e) => {
            eprintln!("Error creating custom record: {}", e);
            "err".to_string()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_create_custom_record() {
        let result = create_custom_record("test data".to_string()).await;
        assert_ne!(result, "err");
    }
}
```

## 移行ガイド

### 既存コードからの移行

#### Before (旧実装)
```rust
extern crate reqwest;
use crate::data_refresh;

pub async fn old_request() -> String {
    let token = data_refresh(&"access");
    let client = reqwest::Client::new();
    let res = client
        .post(url)
        .json(&data)
        .header("Authorization", "Bearer ".to_owned() + &token)
        .send()
        .await
        .unwrap()
        .text()
        .await
        .unwrap();
    res
}
```

#### After (新実装)
```rust
use crate::http_client::HttpClient;

pub async fn new_request() -> String {
    let client = HttpClient::new();
    match client.post_json_with_auth(&url, &data).await {
        Ok(response) => response,
        Err(e) => {
            eprintln!("Error in request: {}", e);
            "err".to_string()
        }
    }
}
```

### チェックリスト
- [ ] `extern crate reqwest;`を削除
- [ ] `use crate::http_client::HttpClient;`を追加
- [ ] `data_refresh(&"access")`の手動呼び出しを削除
- [ ] `reqwest::Client::new()`を`HttpClient::new()`に置換
- [ ] `.unwrap()`を適切な`match`文に置換
- [ ] エラーメッセージの追加