feat: remove anyhow dependency and replace with custom error handling; update validation error types

main
Yoo1tic 2025-07-28 19:06:37 +08:00
parent 9e88973ef7
commit 660367d8a3
10 changed files with 42 additions and 34 deletions

7
Cargo.lock generated
View File

@ -76,12 +76,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "async-stream"
version = "0.3.6"
@ -387,7 +381,6 @@ dependencies = [
name = "gemini-keychecker"
version = "0.1.0"
dependencies = [
"anyhow",
"async-stream",
"backon",
"clap",

View File

@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
mimalloc = { version = "*" }
anyhow = "1.0"
backon = "1"
clap = { version = "4.5", features = ["derive"] }
futures = "0.3"

View File

@ -1,6 +1,6 @@
use crate::adapters::output::write_keys_to_file;
use crate::types::GeminiKey;
use anyhow::Result;
use crate::error::Result;
use std::{collections::HashSet, fs, path::Path, str::FromStr};
/// Load and validate API keys from a file

View File

@ -1,5 +1,5 @@
use crate::types::GeminiKey;
use anyhow::Result;
use crate::error::Result;
use std::{fs, io::Write};
use tokio::io::{AsyncWriteExt, BufWriter};
use toml::Value;

View File

@ -1,4 +1,4 @@
use anyhow::Result;
use crate::error::ValidationError;
use clap::Parser;
use figment::{
Figment,
@ -85,7 +85,7 @@ impl Default for KeyCheckerConfig {
}
}
impl KeyCheckerConfig {
pub fn load_config() -> Result<Self> {
pub fn load_config() -> Result<Self, ValidationError> {
// Define the path to the configuration file
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| "Config.toml".into());
@ -167,8 +167,6 @@ pub static TEST_MESSAGE_BODY: LazyLock<Value> = LazyLock::new(|| {
})
});
fn default_api_host() -> Url {
DEFAULT_CONFIG.api_host.clone()
}

View File

@ -3,13 +3,31 @@ use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("HTTP error: {0}")]
HttpRequest(#[from] reqwest::Error),
ReqwestError(#[from] reqwest::Error),
#[error("Key is unavailable or invalid")]
KeyUnavailable,
KeyInvalid,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Configuration error: {0}")]
ConfigError(#[from] figment::Error),
#[error("TOML serialization error: {0}")]
TomlSer(#[from] toml::ser::Error),
#[error("TOML deserialization error: {0}")]
TomlDe(#[from] toml::de::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("URL parse error: {0}")]
UrlParse(#[from] url::ParseError),
#[error("Key validation failed: {0}")]
Invalid(String),
Validation(String),
}
pub type Result<T> = std::result::Result<T, ValidationError>;

View File

@ -1,4 +1,4 @@
use anyhow::Result;
use gemini_keychecker::error::ValidationError;
use gemini_keychecker::{BANNER, config::KeyCheckerConfig, service::start_validation};
use mimalloc::MiMalloc;
@ -8,7 +8,7 @@ static GLOBAL: MiMalloc = MiMalloc;
/// Main function - displays banner and starts validation service
#[tokio::main]
async fn main() -> Result<()> {
async fn main() -> Result<(), ValidationError> {
// Display banner and configuration status at startup
println!("{BANNER}");

View File

@ -1,8 +1,8 @@
use crate::config::KeyCheckerConfig;
use crate::{config::KeyCheckerConfig, error::ValidationError};
use reqwest::Client;
use std::time::Duration;
pub fn client_builder(config: &KeyCheckerConfig) -> Result<Client, reqwest::Error> {
pub fn client_builder(config: &KeyCheckerConfig) -> Result<Client, ValidationError> {
// Set the maximum number of connections per host based on concurrency.
let pool_size = config.concurrency / 2;
@ -18,5 +18,5 @@ pub fn client_builder(config: &KeyCheckerConfig) -> Result<Client, reqwest::Erro
builder = builder.http1_only();
}
builder.build()
Ok(builder.build()?)
}

View File

@ -21,13 +21,13 @@ pub async fn validate_key(
StatusCode::OK => Ok(api_key),
StatusCode::UNAUTHORIZED
| StatusCode::FORBIDDEN
| StatusCode::TOO_MANY_REQUESTS => Err(ValidationError::KeyUnavailable),
_ => Err(ValidationError::HttpRequest(
| StatusCode::TOO_MANY_REQUESTS => Err(ValidationError::KeyInvalid),
_ => Err(ValidationError::ReqwestError(
response.error_for_status().unwrap_err(),
)),
}
}
Err(e) => Err(ValidationError::HttpRequest(e)),
Err(e) => Err(ValidationError::ReqwestError(e)),
}
}

View File

@ -1,12 +1,12 @@
use anyhow::Result;
use crate::error::ValidationError;
use async_stream::stream;
use futures::{pin_mut, stream::StreamExt};
use reqwest::Client;
use std::time::Instant;
use tokio::{fs, io::AsyncWriteExt, sync::mpsc};
use super::{key_tester::validate_key, http_client::client_builder};
use crate::adapters::{write_keys_txt_file, load_keys};
use super::{http_client::client_builder, key_tester::validate_key};
use crate::adapters::{load_keys, write_keys_txt_file};
use crate::config::KeyCheckerConfig;
use crate::types::GeminiKey;
@ -26,7 +26,7 @@ impl ValidationService {
}
}
pub async fn validate_keys(&self, keys: Vec<GeminiKey>) -> Result<()> {
pub async fn validate_keys(&self, keys: Vec<GeminiKey>) -> Result<(), ValidationError> {
let start_time = Instant::now();
// Create channel for streaming keys from producer to consumer
@ -74,15 +74,15 @@ impl ValidationService {
}
/// 启动验证服务 - 封装了所有启动逻辑
pub async fn start_validation() -> Result<()> {
pub async fn start_validation() -> Result<(), ValidationError> {
let config = KeyCheckerConfig::load_config()?;
// 加载密钥
let keys = load_keys(config.input_path.as_path())?;
// 构建HTTP客户端
let client = client_builder(&config)?;
// 创建验证服务并启动
let validation_service = ValidationService::new(config, client);
validation_service.validate_keys(keys).await