From c97a3d5cda6b620d42f4e661c3d08b35b1a4dff8 Mon Sep 17 00:00:00 2001 From: Jiangzhou He Date: Sun, 9 Nov 2025 11:42:38 -0800 Subject: [PATCH] refactor: move `error` into `utils/src/error.rs` --- Cargo.lock | 1 + rust/cocoindex/src/builder/analyzed_flow.rs | 2 +- rust/cocoindex/src/execution/memoization.rs | 6 +- rust/cocoindex/src/lib_context.rs | 2 +- rust/cocoindex/src/ops/factory_bases.rs | 1 - rust/cocoindex/src/prelude.rs | 4 +- rust/cocoindex/src/service/error.rs | 91 --------------------- rust/cocoindex/src/service/mod.rs | 1 - rust/utils/Cargo.toml | 3 + rust/utils/src/error.rs | 76 +++++++++++++++++ 10 files changed, 86 insertions(+), 101 deletions(-) delete mode 100644 rust/cocoindex/src/service/error.rs diff --git a/Cargo.lock b/Cargo.lock index 8dd0e37b..51aea6fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1409,6 +1409,7 @@ dependencies = [ "anyhow", "async-openai", "async-trait", + "axum 0.8.4", "base64 0.22.1", "blake2", "encoding_rs", diff --git a/rust/cocoindex/src/builder/analyzed_flow.rs b/rust/cocoindex/src/builder/analyzed_flow.rs index 8dfd6adc..65e930ad 100644 --- a/rust/cocoindex/src/builder/analyzed_flow.rs +++ b/rust/cocoindex/src/builder/analyzed_flow.rs @@ -1,7 +1,7 @@ use crate::{ops::interface::FlowInstanceContext, prelude::*}; use super::{analyzer, plan}; -use crate::service::error::{SharedError, SharedResultExt, shared_ok}; +use cocoindex_utils::error::{SharedError, SharedResultExt, shared_ok}; pub struct AnalyzedFlow { pub flow_instance: spec::FlowInstanceSpec, diff --git a/rust/cocoindex/src/execution/memoization.rs b/rust/cocoindex/src/execution/memoization.rs index 9c297099..a5422aff 100644 --- a/rust/cocoindex/src/execution/memoization.rs +++ b/rust/cocoindex/src/execution/memoization.rs @@ -7,10 +7,8 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::{ - base::{schema, value}, - service::error::{SharedError, SharedResultExtRef}, -}; +use crate::base::{schema, value}; +use cocoindex_utils::error::{SharedError, SharedResultExtRef}; use cocoindex_utils::fingerprint::{Fingerprint, Fingerprinter}; #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rust/cocoindex/src/lib_context.rs b/rust/cocoindex/src/lib_context.rs index 7ac249a1..96b569fe 100644 --- a/rust/cocoindex/src/lib_context.rs +++ b/rust/cocoindex/src/lib_context.rs @@ -4,11 +4,11 @@ use crate::prelude::*; use crate::builder::AnalyzedFlow; use crate::execution::source_indexer::SourceIndexingContext; -use crate::service::error::ApiError; use crate::service::query_handler::{QueryHandler, QueryHandlerSpec}; use crate::settings; use crate::setup::ObjectSetupChange; use axum::http::StatusCode; +use cocoindex_utils::error::ApiError; use indicatif::MultiProgress; use sqlx::PgPool; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; diff --git a/rust/cocoindex/src/ops/factory_bases.rs b/rust/cocoindex/src/ops/factory_bases.rs index 491acd9b..ec9857f3 100644 --- a/rust/cocoindex/src/ops/factory_bases.rs +++ b/rust/cocoindex/src/ops/factory_bases.rs @@ -5,7 +5,6 @@ use std::hash::Hash; use super::interface::*; use super::registry::*; -use crate::api_bail; use crate::base::schema::*; use crate::base::spec::*; use crate::builder::plan::AnalyzedValueMapping; diff --git a/rust/cocoindex/src/prelude.rs b/rust/cocoindex/src/prelude.rs index f8354a98..8c959443 100644 --- a/rust/cocoindex/src/prelude.rs +++ b/rust/cocoindex/src/prelude.rs @@ -23,11 +23,11 @@ pub(crate) use crate::builder::{self, exec_ctx, plan}; pub(crate) use crate::execution; pub(crate) use crate::lib_context::{FlowContext, LibContext, get_lib_context, get_runtime}; pub(crate) use crate::ops::interface; -pub(crate) use crate::service::error::{ApiError, invariance_violation}; pub(crate) use crate::setup; pub(crate) use crate::setup::AuthRegistry; -pub(crate) use crate::{api_bail, api_error}; pub(crate) use cocoindex_utils as utils; +pub(crate) use cocoindex_utils::error::{ApiError, invariance_violation}; +pub(crate) use cocoindex_utils::{api_bail, api_error}; pub(crate) use cocoindex_utils::{batching, concur_control, http, retryable}; pub(crate) use anyhow::{anyhow, bail}; diff --git a/rust/cocoindex/src/service/error.rs b/rust/cocoindex/src/service/error.rs deleted file mode 100644 index d397830d..00000000 --- a/rust/cocoindex/src/service/error.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::prelude::*; - -use axum::{ - Json, - http::StatusCode, - response::{IntoResponse, Response}, -}; -use pyo3::{exceptions::PyException, prelude::*}; -use std::{ - error::Error, - fmt::{Debug, Display}, -}; - -// Re-export error types from cocoindex-utils -pub use cocoindex_utils::error::{ - SharedError, SharedResultExt, SharedResultExtRef, invariance_violation, shared_ok, -}; - -#[derive(Debug)] -pub struct ApiError { - pub err: anyhow::Error, - pub status_code: StatusCode, -} - -impl ApiError { - pub fn new(message: &str, status_code: StatusCode) -> Self { - Self { - err: anyhow!("{}", message), - status_code, - } - } -} - -impl Display for ApiError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - Display::fmt(&self.err, f) - } -} - -impl Error for ApiError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - self.err.source() - } -} - -#[derive(Serialize)] -struct ErrorResponse { - error: String, -} - -impl IntoResponse for ApiError { - fn into_response(self) -> Response { - debug!("Internal server error:\n{:?}", self.err); - let error_response = ErrorResponse { - error: format!("{:?}", self.err), - }; - (self.status_code, Json(error_response)).into_response() - } -} - -impl From for ApiError { - fn from(err: anyhow::Error) -> ApiError { - if err.is::() { - return err.downcast::().unwrap(); - } - Self { - err, - status_code: StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl From for PyErr { - fn from(val: ApiError) -> Self { - PyException::new_err(val.err.to_string()) - } -} - -#[macro_export] -macro_rules! api_bail { - ( $fmt:literal $(, $($arg:tt)*)?) => { - return Err($crate::service::error::ApiError::new(&format!($fmt $(, $($arg)*)?), axum::http::StatusCode::BAD_REQUEST).into()) - }; -} - -#[macro_export] -macro_rules! api_error { - ( $fmt:literal $(, $($arg:tt)*)?) => { - $crate::service::error::ApiError::new(&format!($fmt $(, $($arg)*)?), axum::http::StatusCode::BAD_REQUEST) - }; -} diff --git a/rust/cocoindex/src/service/mod.rs b/rust/cocoindex/src/service/mod.rs index e7492ff0..7a8856c4 100644 --- a/rust/cocoindex/src/service/mod.rs +++ b/rust/cocoindex/src/service/mod.rs @@ -1,3 +1,2 @@ -pub(crate) mod error; pub(crate) mod flows; pub(crate) mod query_handler; diff --git a/rust/utils/Cargo.toml b/rust/utils/Cargo.toml index 2257e32b..a9f38df3 100644 --- a/rust/utils/Cargo.toml +++ b/rust/utils/Cargo.toml @@ -11,6 +11,9 @@ anyhow = { workspace = true } async-trait = { workspace = true } log = { workspace = true } +# Web framework +axum = { workspace = true } + # Serialization serde = { workspace = true } serde_json = { workspace = true } diff --git a/rust/utils/src/error.rs b/rust/utils/src/error.rs index 853c7d65..b233a3b1 100644 --- a/rust/utils/src/error.rs +++ b/rust/utils/src/error.rs @@ -1,4 +1,10 @@ use anyhow; +use axum::{ + Json, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use serde::Serialize; use std::{ error::Error, fmt::{Debug, Display}, @@ -134,3 +140,73 @@ impl<'a, T> SharedResultExtRef<'a, T> for &'a Result { pub fn invariance_violation() -> anyhow::Error { anyhow::anyhow!("Invariance violation") } + +// API Error types for HTTP responses + +#[derive(Debug)] +pub struct ApiError { + pub err: anyhow::Error, + pub status_code: StatusCode, +} + +impl ApiError { + pub fn new(message: &str, status_code: StatusCode) -> Self { + Self { + err: anyhow::anyhow!("{}", message), + status_code, + } + } +} + +impl Display for ApiError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + Display::fmt(&self.err, f) + } +} + +impl Error for ApiError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.err.source() + } +} + +#[derive(Serialize)] +struct ErrorResponse { + error: String, +} + +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + log::debug!("Internal server error:\n{:?}", self.err); + let error_response = ErrorResponse { + error: format!("{:?}", self.err), + }; + (self.status_code, Json(error_response)).into_response() + } +} + +impl From for ApiError { + fn from(err: anyhow::Error) -> ApiError { + if err.is::() { + return err.downcast::().unwrap(); + } + Self { + err, + status_code: StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +#[macro_export] +macro_rules! api_bail { + ( $fmt:literal $(, $($arg:tt)*)?) => { + return Err($crate::error::ApiError::new(&format!($fmt $(, $($arg)*)?), axum::http::StatusCode::BAD_REQUEST).into()) + }; +} + +#[macro_export] +macro_rules! api_error { + ( $fmt:literal $(, $($arg:tt)*)?) => { + $crate::error::ApiError::new(&format!($fmt $(, $($arg)*)?), axum::http::StatusCode::BAD_REQUEST) + }; +}