From 7b2adffb46418c0b4dbe76f4747834ed4ccaa7ec Mon Sep 17 00:00:00 2001 From: NetworkCats Date: Mon, 2 Mar 2026 20:00:19 +0800 Subject: [PATCH] feat: add CLI user-agent detection and error types --- src/cli_detect.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 19 ++++++++++++++ src/main.rs | 2 ++ 3 files changed, 88 insertions(+) create mode 100644 src/cli_detect.rs create mode 100644 src/error.rs diff --git a/src/cli_detect.rs b/src/cli_detect.rs new file mode 100644 index 0000000..4d4f6af --- /dev/null +++ b/src/cli_detect.rs @@ -0,0 +1,67 @@ +const CLI_PREFIXES: &[&str] = &[ + "curl/", + "Wget/", + "HTTPie/", + "xh/", + "fetch", + "PowerShell/", + "WindowsPowerShell/", +]; + +pub fn is_cli_user_agent(user_agent: &str) -> bool { + CLI_PREFIXES + .iter() + .any(|prefix| user_agent.starts_with(prefix)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detects_curl() { + assert!(is_cli_user_agent("curl/8.7.1")); + } + + #[test] + fn detects_wget() { + assert!(is_cli_user_agent("Wget/1.21.4")); + } + + #[test] + fn detects_httpie() { + assert!(is_cli_user_agent("HTTPie/3.2.2")); + } + + #[test] + fn detects_xh() { + assert!(is_cli_user_agent("xh/0.22.0")); + } + + #[test] + fn detects_fetch() { + assert!(is_cli_user_agent("fetch libfetch/2.0")); + } + + #[test] + fn detects_powershell() { + assert!(is_cli_user_agent("PowerShell/7.4.1")); + } + + #[test] + fn detects_windows_powershell() { + assert!(is_cli_user_agent("WindowsPowerShell/5.1")); + } + + #[test] + fn rejects_browser() { + assert!(!is_cli_user_agent( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + )); + } + + #[test] + fn rejects_empty() { + assert!(!is_cli_user_agent("")); + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..510ce21 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,19 @@ +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; + +pub enum AppError { + IpNotFound, + InvalidIp, + DbLookupFailed, +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + let (status, message) = match self { + Self::IpNotFound => (StatusCode::NOT_FOUND, "IP not found in database"), + Self::InvalidIp => (StatusCode::BAD_REQUEST, "Invalid IP address"), + Self::DbLookupFailed => (StatusCode::INTERNAL_SERVER_ERROR, "Database lookup failed"), + }; + (status, message).into_response() + } +} diff --git a/src/main.rs b/src/main.rs index 0f18600..fda839e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ +mod cli_detect; mod config; mod db; +mod error; mod models; mod updater;