HTTPステータスを猫と表示するCLIツール「httpcat」
はじめに
HTTPリクエストを送信してステータスコードを確認する――開発者にとって日常的なタスクです。curlやwgetを使うのも良いですが、せっかくなら楽しく確認したいと思いませんか?
今回は、HTTPリクエストを送信し、レスポンスのステータスコードに応じた猫画像をターミナルに表示するCLIツール「httpcat」をRustで開発しました。この記事では、その実装過程と技術的なポイントを紹介します。
httpcatとは
httpcatは、指定したURLにHTTPリクエストを送信し、http.catから取得した猫画像をターミナルに直接表示するCLIツールです。ステータスコードに応じた猫画像が表示されます(例: 200 OK、404 Not Found、500 Internal Server Errorなど)。
使い方

基本的な使い方はシンプルです:
$ httpcat https://example.com
🌐 Requesting: https://example.com
⏳ Loading...
✅ 200 OK
🐱 Here's your cat:
[ターミナルに猫画像が表示される]
⏱️ Response time: 234ms
主な機能
- HTTPリクエストの送信(GET/POST/PUT/DELETE等に対応)
- ステータスコードに応じた猫画像のターミナル表示
- ASCII Artモードでの画像表示
- レスポンスヘッダー・ボディの表示
- カスタムヘッダーの追加
- レスポンス時間の計測
技術スタック
このプロジェクトでは以下のクレートを使用しました:
- reqwest: HTTP通信を担当
- tokio: 非同期ランタイム
- image: 画像処理
- viuer: ターミナルでの画像表示
- clap: CLIの引数パーサー
- anyhow: エラーハンドリング
- colored: カラー出力
実装のポイント
1. モジュール構成
機能ごとにモジュールを分割し、保守性を高めました:
src/
├── main.rs # エントリーポイント
├── cli.rs # CLI引数の定義
├── http.rs # HTTPリクエスト処理
├── cat.rs # 猫画像の取得・表示
├── display.rs # ターミナル出力
└── ascii.rs # ASCII Art変換
2. HTTPリクエストの実装
reqwestのブロッキングクライアントを使い、シンプルかつ堅牢なHTTP通信を実装しました:
pub fn send_request(
url: &str,
method: &str,
request_headers: &[String],
) -> Result<HttpResponse> {
let client = Client::builder()
.timeout(Duration::from_secs(30))
.build()
.context("Failed to create HTTP client")?;
let start = Instant::now();
let response = match method.to_uppercase().as_str() {
"GET" => client.get(url),
"POST" => client.post(url),
"PUT" => client.put(url),
"DELETE" => client.delete(url),
// ... その他のメソッド
_ => anyhow::bail!("Unsupported HTTP method: {}", method),
}
.headers(headers)
.send()
.context("Failed to send HTTP request")?;
let duration = start.elapsed();
let status = response.status().as_u16();
let body = response.text().unwrap_or_default();
Ok(HttpResponse {
status,
status_text,
headers: response_headers,
body,
duration,
})
}
レスポンス時間を計測するため、Instant::now()でリクエスト前の時刻を記録し、レスポンス取得後に経過時間を計算しています。
3. ターミナルでの画像表示
viuerクレートを使うことで、ターミナルに直接画像を表示できます:
pub fn display_image(img: &DynamicImage, width: u32) -> Result<()> {
let config = viuer::Config {
width: Some(width),
absolute_offset: false,
..Default::default()
};
viuer::print(img, &config)
.context("Failed to display image in terminal")?;
Ok(())
}
画像の幅を指定できるため、ターミナルのサイズに合わせた表示が可能です。
4. ASCII Art変換機能
--asciiオプションを使うと、画像をASCII Artに変換して表示できます:
pub fn image_to_ascii(img: &DynamicImage, width: u32) -> String {
let aspect_ratio = img.height() as f32 / img.width() as f32;
let height = (width as f32 * aspect_ratio * 0.5) as u32;
let resized = img.resize_exact(
width,
height,
image::imageops::FilterType::Lanczos3,
);
let gray = resized.to_luma8();
let mut ascii_art = String::new();
for y in 0..height {
for x in 0..width {
let pixel = gray.get_pixel(x, y)[0];
let char_index = (pixel as usize * (ASCII_CHARS.len() - 1)) / 255;
ascii_art.push(ASCII_CHARS[char_index]);
}
ascii_art.push('\n');
}
ascii_art
}
画像をグレースケールに変換し、各ピクセルの明度に応じてASCII文字を割り当てています。
5. ステータスコードに応じた色分け
coloredクレートを使い、ステータスコードごとに視覚的に分かりやすい出力を実装しました:
pub fn print_response_status(response: &HttpResponse) {
let colored_status = if response.status >= 200 && response.status < 300 {
format!("✅ {}", response.status_text).green()
} else if response.status >= 300 && response.status < 400 {
format!("➡️ {}", response.status_text).yellow()
} else if response.status >= 400 && response.status < 500 {
format!("❌ {}", response.status_text).red()
} else if response.status >= 500 {
format!("💥 {}", response.status_text).bright_red()
} else {
format!("ℹ️ {}", response.status_text).white()
};
println!("{}\n", colored_status);
}
成功は緑、クライアントエラーは赤、サーバーエラーは明るい赤で表示することで、一目でステータスを把握できます。
6. CLIインターフェース
clapのderiveマクロを使い、型安全なCLIを実装しました:
#[derive(Parser, Debug)]
#[command(name = "httpcat")]
#[command(about = "HTTP request tool with cat images based on status codes")]
pub struct Cli {
/// Target URL to send HTTP request
pub url: String,
/// HTTP method to use
#[arg(short = 'X', long, default_value = "GET")]
pub method: String,
/// Image width in terminal
#[arg(long, default_value_t = 60)]
pub size: u32,
/// Display image as ASCII art
#[arg(long)]
pub ascii: bool,
/// Don't display cat image
#[arg(long)]
pub no_image: bool,
/// Show response headers
#[arg(long)]
pub headers: bool,
/// Show response body
#[arg(long)]
pub body: bool,
/// Add request header (can be used multiple times)
#[arg(short = 'H', long = "header")]
pub request_headers: Vec<String>,
}
使用例
POSTリクエストの送信
httpcat -X POST https://api.example.com/data
カスタムヘッダー付きリクエスト
httpcat -H "Authorization: Bearer token123" https://api.example.com
レスポンス詳細の表示
httpcat --headers --body https://example.com
ASCII Artモード
httpcat --ascii https://example.com
ステータス確認のみ(画像なし)
httpcat --no-image https://example.com
以下のコマンドで本コマンドをインストールできます:
cargo install httpcat
まとめ
「猫画像でHTTPステータスを確認する」というコンセプトで、実装を通じてRustの実践的な技術を学べました。
APIのデバッグやヘルスチェックの際、ぜひhttpcatを使ってみてください。きっと猫たちが開発を楽しくしてくれるはずです!
$ httpcat https://api.github.com
🌐 Requesting: https://api.github.com
⏳ Loading...
✅ 200 OK
🐱 Here's your cat:
[猫の画像]
⏱️ Response time: 123ms
インストール方法
httpcatはcrates.ioで公開されており、cargoを使って簡単にインストールできます:
$ cargo install httpcat
インストール後は、すぐに使い始められます:
$ httpcat https://zenn.dev/justhiro/articles/7cb145b58bffc0
Discussion