use axum::Json; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use config::ConfigError; use serde_json::json; use thiserror::Error; #[derive(Error, Debug)] pub enum VoiceCliError { #[error("{0}")] Config(String), #[error("{0}")] AudioProcessing(String), #[error("{0}")] Transcription(String), #[error("{0}")] Model(String), #[error("{0}")] FileIo(String), #[error("{0}")] Http(String), #[error("{0}")] Serialization(String), #[error("{0}")] Json(String), #[error("{0}")] ConfigRs(String), #[error("{0}")] Daemon(String), #[error("{0}")] UnsupportedFormat(String), #[error("{0}")] FileTooLarge(String), #[error("{0}")] ModelNotFound(String), #[error("{0}")] InvalidModelName(String), // Worker pool related errors #[error("{0}")] WorkerPoolError(String), #[error("{0}")] TranscriptionTimeout(String), #[error("{0}")] TranscriptionFailed(String), // Audio processing specific errors #[error("{0}")] AudioConversionFailed(String), #[error("{0}")] AudioProbeError(String), #[error("{0}")] TempFileError(String), // Multipart form handling errors #[error("{0}")] MultipartError(String), #[error("{0}")] MissingField(String), // Network related errors #[error("{0}")] Network(String), // Storage related errors #[error("{0}")] Storage(String), // Task management errors #[error("{0}")] TaskManagementDisabled(String), #[error("{0}")] NotFound(String), #[error("{0}")] Initialization(String), // TTS related errors #[error("{0}")] TtsError(String), #[error("{0}")] InvalidInput(String), #[error("{0}")] Io(String), } impl VoiceCliError { // =========================================== // 工厂方法 - 创建带国际化消息的错误 // =========================================== pub fn config_error(detail: impl Into) -> Self { Self::Config(t!("errors.voice.config", detail = detail.into()).to_string()) } pub fn audio_processing_error(detail: impl Into) -> Self { Self::AudioProcessing( t!("errors.voice.audio_processing", detail = detail.into()).to_string(), ) } pub fn transcription_error(detail: impl Into) -> Self { Self::Transcription(t!("errors.voice.transcription", detail = detail.into()).to_string()) } pub fn model_error(detail: impl Into) -> Self { Self::Model(t!("errors.voice.model", detail = detail.into()).to_string()) } pub fn file_io_error(detail: impl Into) -> Self { Self::FileIo(t!("errors.voice.file_io", detail = detail.into()).to_string()) } pub fn http_error(detail: impl Into) -> Self { Self::Http(t!("errors.voice.http", detail = detail.into()).to_string()) } pub fn serialization_error(detail: impl Into) -> Self { Self::Serialization(t!("errors.voice.serialization", detail = detail.into()).to_string()) } pub fn json_error(detail: impl Into) -> Self { Self::Json(t!("errors.voice.json", detail = detail.into()).to_string()) } pub fn config_rs_error(detail: impl Into) -> Self { Self::ConfigRs(t!("errors.voice.config_rs", detail = detail.into()).to_string()) } pub fn daemon_error(detail: impl Into) -> Self { Self::Daemon(t!("errors.voice.daemon", detail = detail.into()).to_string()) } pub fn unsupported_format(detail: impl Into) -> Self { Self::UnsupportedFormat( t!("errors.voice.unsupported_format", detail = detail.into()).to_string(), ) } pub fn file_too_large(size: usize, max: usize) -> Self { Self::FileTooLarge(t!("errors.voice.file_too_large", size = size, max = max).to_string()) } pub fn model_not_found(model: impl Into) -> Self { Self::ModelNotFound(t!("errors.voice.model_not_found", model = model.into()).to_string()) } pub fn invalid_model_name(model: impl Into) -> Self { Self::InvalidModelName( t!("errors.voice.invalid_model_name", model = model.into()).to_string(), ) } pub fn worker_pool_error(detail: impl Into) -> Self { Self::WorkerPoolError(t!("errors.voice.worker_pool", detail = detail.into()).to_string()) } pub fn transcription_timeout(seconds: u64) -> Self { Self::TranscriptionTimeout( t!("errors.voice.transcription_timeout", seconds = seconds).to_string(), ) } pub fn transcription_failed(detail: impl Into) -> Self { Self::TranscriptionFailed( t!("errors.voice.transcription_failed", detail = detail.into()).to_string(), ) } pub fn audio_conversion_failed(detail: impl Into) -> Self { Self::AudioConversionFailed( t!( "errors.voice.audio_conversion_failed", detail = detail.into() ) .to_string(), ) } pub fn audio_probe_error(detail: impl Into) -> Self { Self::AudioProbeError( t!("errors.voice.audio_probe_error", detail = detail.into()).to_string(), ) } pub fn temp_file_error(detail: impl Into) -> Self { Self::TempFileError(t!("errors.voice.temp_file_error", detail = detail.into()).to_string()) } pub fn multipart_error(detail: impl Into) -> Self { Self::MultipartError(t!("errors.voice.multipart_error", detail = detail.into()).to_string()) } pub fn missing_field(field: impl Into) -> Self { Self::MissingField(t!("errors.voice.missing_field", field = field.into()).to_string()) } pub fn network_error(detail: impl Into) -> Self { Self::Network(t!("errors.voice.network", detail = detail.into()).to_string()) } pub fn storage_error(detail: impl Into) -> Self { Self::Storage(t!("errors.voice.storage", detail = detail.into()).to_string()) } pub fn task_management_disabled() -> Self { Self::TaskManagementDisabled(t!("errors.voice.task_management_disabled").to_string()) } pub fn not_found(resource: impl Into) -> Self { Self::NotFound(t!("errors.voice.not_found", resource = resource.into()).to_string()) } pub fn initialization_error(detail: impl Into) -> Self { Self::Initialization(t!("errors.voice.initialization", detail = detail.into()).to_string()) } pub fn tts_error(detail: impl Into) -> Self { Self::TtsError(t!("errors.voice.tts", detail = detail.into()).to_string()) } pub fn invalid_input(detail: impl Into) -> Self { Self::InvalidInput(t!("errors.voice.invalid_input", detail = detail.into()).to_string()) } pub fn io_error(detail: impl Into) -> Self { Self::Io(t!("errors.voice.io", detail = detail.into()).to_string()) } } // =========================================== // 从外部错误类型转换 // =========================================== impl From for VoiceCliError { fn from(err: std::io::Error) -> Self { Self::file_io_error(err.to_string()) } } impl From for VoiceCliError { fn from(err: reqwest::Error) -> Self { Self::http_error(err.to_string()) } } impl From for VoiceCliError { fn from(err: serde_yaml::Error) -> Self { Self::serialization_error(err.to_string()) } } impl From for VoiceCliError { fn from(err: serde_json::Error) -> Self { Self::json_error(err.to_string()) } } impl From for VoiceCliError { fn from(err: ConfigError) -> Self { Self::config_rs_error(err.to_string()) } } impl IntoResponse for VoiceCliError { fn into_response(self) -> Response { let (status, error_message) = match self { VoiceCliError::Config(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::AudioProcessing(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::Transcription(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::Model(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::FileIo(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::Http(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::Serialization(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::Json(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::Daemon(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::UnsupportedFormat(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::FileTooLarge(_) => (StatusCode::PAYLOAD_TOO_LARGE, self.to_string()), VoiceCliError::ModelNotFound(_) => (StatusCode::NOT_FOUND, self.to_string()), VoiceCliError::InvalidModelName(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::WorkerPoolError(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::TranscriptionTimeout(_) => { (StatusCode::REQUEST_TIMEOUT, self.to_string()) } VoiceCliError::TranscriptionFailed(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::AudioConversionFailed(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::AudioProbeError(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::TempFileError(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::MultipartError(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::MissingField(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::Network(_) => (StatusCode::BAD_GATEWAY, self.to_string()), VoiceCliError::Storage(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::ConfigRs(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::TaskManagementDisabled(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::NotFound(_) => (StatusCode::NOT_FOUND, self.to_string()), VoiceCliError::Initialization(_) => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()) } VoiceCliError::TtsError(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), VoiceCliError::InvalidInput(_) => (StatusCode::BAD_REQUEST, self.to_string()), VoiceCliError::Io(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), }; let body = Json(json!({ "error": error_message, "status": status.as_u16() })); (status, body).into_response() } } pub type Result = std::result::Result;