feat: remove anyhow dependency and replace with custom error handling; update validation error types
parent
9e88973ef7
commit
660367d8a3
|
@ -76,12 +76,6 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"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]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -387,7 +381,6 @@ dependencies = [
|
||||||
name = "gemini-keychecker"
|
name = "gemini-keychecker"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"backon",
|
"backon",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
|
@ -5,7 +5,6 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mimalloc = { version = "*" }
|
mimalloc = { version = "*" }
|
||||||
anyhow = "1.0"
|
|
||||||
backon = "1"
|
backon = "1"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::adapters::output::write_keys_to_file;
|
use crate::adapters::output::write_keys_to_file;
|
||||||
use crate::types::GeminiKey;
|
use crate::types::GeminiKey;
|
||||||
use anyhow::Result;
|
use crate::error::Result;
|
||||||
use std::{collections::HashSet, fs, path::Path, str::FromStr};
|
use std::{collections::HashSet, fs, path::Path, str::FromStr};
|
||||||
|
|
||||||
/// Load and validate API keys from a file
|
/// Load and validate API keys from a file
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::types::GeminiKey;
|
use crate::types::GeminiKey;
|
||||||
use anyhow::Result;
|
use crate::error::Result;
|
||||||
use std::{fs, io::Write};
|
use std::{fs, io::Write};
|
||||||
use tokio::io::{AsyncWriteExt, BufWriter};
|
use tokio::io::{AsyncWriteExt, BufWriter};
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::Result;
|
use crate::error::ValidationError;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use figment::{
|
use figment::{
|
||||||
Figment,
|
Figment,
|
||||||
|
@ -85,7 +85,7 @@ impl Default for KeyCheckerConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl KeyCheckerConfig {
|
impl KeyCheckerConfig {
|
||||||
pub fn load_config() -> Result<Self> {
|
pub fn load_config() -> Result<Self, ValidationError> {
|
||||||
// Define the path to the configuration file
|
// Define the path to the configuration file
|
||||||
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| "Config.toml".into());
|
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 {
|
fn default_api_host() -> Url {
|
||||||
DEFAULT_CONFIG.api_host.clone()
|
DEFAULT_CONFIG.api_host.clone()
|
||||||
}
|
}
|
||||||
|
|
26
src/error.rs
26
src/error.rs
|
@ -3,13 +3,31 @@ use thiserror::Error;
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ValidationError {
|
pub enum ValidationError {
|
||||||
#[error("HTTP error: {0}")]
|
#[error("HTTP error: {0}")]
|
||||||
HttpRequest(#[from] reqwest::Error),
|
ReqwestError(#[from] reqwest::Error),
|
||||||
|
|
||||||
#[error("Key is unavailable or invalid")]
|
#[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}")]
|
#[error("Key validation failed: {0}")]
|
||||||
Invalid(String),
|
Validation(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, ValidationError>;
|
pub type Result<T> = std::result::Result<T, ValidationError>;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::Result;
|
use gemini_keychecker::error::ValidationError;
|
||||||
use gemini_keychecker::{BANNER, config::KeyCheckerConfig, service::start_validation};
|
use gemini_keychecker::{BANNER, config::KeyCheckerConfig, service::start_validation};
|
||||||
|
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
|
@ -8,7 +8,7 @@ static GLOBAL: MiMalloc = MiMalloc;
|
||||||
|
|
||||||
/// Main function - displays banner and starts validation service
|
/// Main function - displays banner and starts validation service
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<(), ValidationError> {
|
||||||
// Display banner and configuration status at startup
|
// Display banner and configuration status at startup
|
||||||
println!("{BANNER}");
|
println!("{BANNER}");
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::config::KeyCheckerConfig;
|
use crate::{config::KeyCheckerConfig, error::ValidationError};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use std::time::Duration;
|
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.
|
// Set the maximum number of connections per host based on concurrency.
|
||||||
let pool_size = config.concurrency / 2;
|
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 = builder.http1_only();
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.build()
|
Ok(builder.build()?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,13 @@ pub async fn validate_key(
|
||||||
StatusCode::OK => Ok(api_key),
|
StatusCode::OK => Ok(api_key),
|
||||||
StatusCode::UNAUTHORIZED
|
StatusCode::UNAUTHORIZED
|
||||||
| StatusCode::FORBIDDEN
|
| StatusCode::FORBIDDEN
|
||||||
| StatusCode::TOO_MANY_REQUESTS => Err(ValidationError::KeyUnavailable),
|
| StatusCode::TOO_MANY_REQUESTS => Err(ValidationError::KeyInvalid),
|
||||||
_ => Err(ValidationError::HttpRequest(
|
_ => Err(ValidationError::ReqwestError(
|
||||||
response.error_for_status().unwrap_err(),
|
response.error_for_status().unwrap_err(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(ValidationError::HttpRequest(e)),
|
Err(e) => Err(ValidationError::ReqwestError(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use anyhow::Result;
|
use crate::error::ValidationError;
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use futures::{pin_mut, stream::StreamExt};
|
use futures::{pin_mut, stream::StreamExt};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tokio::{fs, io::AsyncWriteExt, sync::mpsc};
|
use tokio::{fs, io::AsyncWriteExt, sync::mpsc};
|
||||||
|
|
||||||
use super::{key_tester::validate_key, http_client::client_builder};
|
use super::{http_client::client_builder, key_tester::validate_key};
|
||||||
use crate::adapters::{write_keys_txt_file, load_keys};
|
use crate::adapters::{load_keys, write_keys_txt_file};
|
||||||
use crate::config::KeyCheckerConfig;
|
use crate::config::KeyCheckerConfig;
|
||||||
use crate::types::GeminiKey;
|
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();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
// Create channel for streaming keys from producer to consumer
|
// 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 config = KeyCheckerConfig::load_config()?;
|
||||||
|
|
||||||
// 加载密钥
|
// 加载密钥
|
||||||
let keys = load_keys(config.input_path.as_path())?;
|
let keys = load_keys(config.input_path.as_path())?;
|
||||||
|
|
||||||
// 构建HTTP客户端
|
// 构建HTTP客户端
|
||||||
let client = client_builder(&config)?;
|
let client = client_builder(&config)?;
|
||||||
|
|
||||||
// 创建验证服务并启动
|
// 创建验证服务并启动
|
||||||
let validation_service = ValidationService::new(config, client);
|
let validation_service = ValidationService::new(config, client);
|
||||||
validation_service.validate_keys(keys).await
|
validation_service.validate_keys(keys).await
|
||||||
|
|
Loading…
Reference in New Issue