diff --git a/roapi/src/config.rs b/roapi/src/config.rs index b6d2193..126eeb2 100644 --- a/roapi/src/config.rs +++ b/roapi/src/config.rs @@ -136,7 +136,7 @@ fn config_arg() -> clap::Arg { .short('c') } -fn get_cmd() -> clap::Command { +pub fn get_cmd() -> clap::Command { clap::Command::new("roapi") .version(env!("CARGO_PKG_VERSION")) .author("QP Hou") @@ -156,8 +156,8 @@ fn get_cmd() -> clap::Command { ]) } -pub fn get_configuration() -> Result { - let matches = get_cmd().get_matches(); +pub fn get_configuration(cmd: clap::Command) -> Result { + let matches = cmd.get_matches(); let mut config: Config = match matches.get_one::("config") { None => Config::default(), diff --git a/roapi/src/context.rs b/roapi/src/context.rs index 3aaa777..881e38c 100644 --- a/roapi/src/context.rs +++ b/roapi/src/context.rs @@ -12,12 +12,22 @@ use columnq::error::QueryError; use columnq::table::TableSource; use columnq::ColumnQ; use log::info; -use snafu::{whatever, Whatever}; +use snafu::prelude::*; use tokio::sync::RwLock; use crate::config::Config; use crate::error::ApiErrResp; +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("No table nor kvstore found in config"))] + NoData, + #[snafu(display("Failed to load table: {source}"))] + LoadTable { source: ColumnQError }, + #[snafu(display("Failed to load kvstore: {source}"))] + LoadKvstore { source: ColumnQError }, +} + pub struct RawRoapiContext { pub cq: ColumnQ, pub response_format: encoding::ContentType, @@ -25,25 +35,25 @@ pub struct RawRoapiContext { } impl RawRoapiContext { - pub async fn new(config: &Config, read_only: bool) -> Result { + pub async fn new(config: &Config, read_only: bool) -> Result { let mut cq = match config.get_datafusion_config() { Ok(df_cfg) => ColumnQ::new_with_config(df_cfg, read_only), _ => ColumnQ::new_with_read_only(read_only), }; if config.tables.is_empty() && config.kvstores.is_empty() { - whatever!("No table nor kvstore found in config"); + return Err(Error::NoData); } for t in config.tables.iter() { info!("loading `{}` as table `{}`", t.io_source, t.name); - whatever!(cq.load_table(t).await, "Failed to load table"); + cq.load_table(t).await.context(LoadTableSnafu)?; info!("registered `{}` as table `{}`", t.io_source, t.name); } for k in config.kvstores.iter() { info!("loading `{}` as kv store `{}`", k.io_source, k.name); - whatever!(cq.load_kv(k.clone()).await, "Failed to load kvstore"); + cq.load_kv(k.clone()).await.context(LoadKvstoreSnafu)?; info!("registered `{}` as kv store `{}`", k.io_source, k.name); } diff --git a/roapi/src/main.rs b/roapi/src/main.rs index 20fef33..a41e7fd 100644 --- a/roapi/src/main.rs +++ b/roapi/src/main.rs @@ -1,24 +1,28 @@ #![deny(warnings)] -use roapi::config::get_configuration; +use roapi::config::{get_cmd, get_configuration}; use roapi::startup::Application; -use snafu::{whatever, Whatever}; #[cfg(feature = "snmalloc")] #[global_allocator] static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc; #[tokio::main] -async fn main() -> Result<(), Whatever> { +async fn main() { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - let config = whatever!(get_configuration(), "Failed to load configuration"); - let application = Application::build(config) - .await - .expect("Failed to build App"); - application - .run_until_stopped() - .await - .expect("Failed to run App"); - Ok(()) + let mut cmd = get_cmd(); + + let re: Result<(), Box> = async { + let cmd = cmd.clone(); + let config = get_configuration(cmd)?; + let application = Application::build(config).await?; + application.run_until_stopped().await?; + Ok(()) + } + .await; + if let Err(e) = re { + cmd.error(clap::error::ErrorKind::Io, format!("{}", e)) + .exit(); + } } diff --git a/roapi/src/startup.rs b/roapi/src/startup.rs index 5d173fc..348a7d1 100644 --- a/roapi/src/startup.rs +++ b/roapi/src/startup.rs @@ -19,6 +19,8 @@ pub enum Error { BuildHttpServer { source: server::http::Error }, #[snafu(display("Failed to build FlightSQL server: {source}"))] BuildFlightSqlServer { source: server::flight_sql::Error }, + #[snafu(display("Failed to build context: {source}"))] + BuildContext { source: crate::context::Error }, } // TODO: replace table reloader with the new concurrent refresh infra @@ -61,7 +63,7 @@ impl Application { let handler_ctx = RawRoapiContext::new(&config, !config.disable_read_only) .await - .expect("Failed to create Roapi context"); + .context(BuildContextSnafu)?; let tables = config .tables