From 40246d1ae09942bd9cc1fe8050d33478cd06f426 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sun, 22 Jun 2025 23:04:52 +0200 Subject: [PATCH] Add library mode --- Cargo.toml | 8 +++ src/chdig.rs | 78 +++++++++++++++++++++++++++ src/interpreter/background_runner.rs | 6 +-- src/interpreter/context.rs | 2 +- src/interpreter/worker.rs | 2 +- src/lib.rs | 15 ++---- src/main.rs | 80 +--------------------------- src/utils.rs | 2 +- src/view/navigation.rs | 4 +- src/view/processes_view.rs | 14 ++--- 10 files changed, 109 insertions(+), 102 deletions(-) create mode 100644 src/chdig.rs diff --git a/Cargo.toml b/Cargo.toml index 826133d..a531279 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,14 @@ license = "MIT" version = "25.4.1" edition = "2021" +[lib] +name = "chdig" +path = "src/lib.rs" + +[[bin]] +name = "chdig" +path = "src/main.rs" + [features] default = ["tls"] tls = ["clickhouse-rs/tls-rustls"] diff --git a/src/chdig.rs b/src/chdig.rs new file mode 100644 index 0000000..0b6a34f --- /dev/null +++ b/src/chdig.rs @@ -0,0 +1,78 @@ +use anyhow::Result; +use backtrace::Backtrace; +use flexi_logger::{LogSpecification, Logger}; +use std::panic::{self, PanicHookInfo}; +use std::sync::Arc; + +use crate::{ + interpreter::{options, ClickHouse, Context, ContextArc}, + view::Navigation, +}; + +fn panic_hook(info: &PanicHookInfo<'_>) { + let location = info.location().unwrap(); + + let msg = match info.payload().downcast_ref::<&'static str>() { + Some(s) => *s, + None => match info.payload().downcast_ref::() { + Some(s) => &s[..], + None => "Box", + }, + }; + + // NOTE: we need to add \r since the terminal is in raw mode. + // (another option is to restore the terminal state with termios) + let stacktrace: String = format!("{:?}", Backtrace::new()).replace('\n', "\n\r"); + + print!( + "\n\rthread '' panicked at '{}', {}\n\r{}", + msg, location, stacktrace + ); +} + +pub async fn chdig_main() -> Result<()> { + let options = options::parse()?; + + // Initialize it before any backends (otherwise backend will prepare terminal for TUI app, and + // panic hook will clear the screen). + let clickhouse = Arc::new(ClickHouse::new(options.clickhouse.clone()).await?); + + panic::set_hook(Box::new(|info| { + panic_hook(info); + })); + + #[cfg(not(target_family = "windows"))] + let backend = cursive::backends::termion::Backend::init()?; + #[cfg(target_family = "windows")] + let backend = cursive::backends::crossterm::Backend::init()?; + + let buffered_backend = Box::new(cursive_buffered_backend::BufferedBackend::new(backend)); + let mut siv = cursive::CursiveRunner::new(cursive::Cursive::new(), buffered_backend); + + // Override with RUST_LOG + // + // NOTE: hyper also has trace_span() which will not be overwritten + // + // FIXME: should be initialize before options, but options prints completion that should be + // done before terminal switched to raw mode. + let logger = Logger::try_with_env_or_str( + "trace,cursive=info,clickhouse_rs=info,skim=info,tuikit=info,hyper=info,rustls=info", + )? + .log_to_writer(cursive_flexi_logger_view::cursive_flexi_logger(&siv)) + .format(flexi_logger::colored_with_thread) + .start()?; + + // FIXME: should be initialized before cursive, otherwise on error it clears the terminal. + let context: ContextArc = Context::new(options, clickhouse, siv.cb_sink().clone()).await?; + + siv.chdig(context.clone()); + + log::info!("chdig started"); + siv.run(); + + // Suppress error from the cursive_flexi_logger_view - "cursive callback sink is closed!" + // Note, cursive_flexi_logger_view does not implements shutdown() so it will not help. + logger.set_new_spec(LogSpecification::parse("none").unwrap()); + + return Ok(()); +} diff --git a/src/interpreter/background_runner.rs b/src/interpreter/background_runner.rs index 06f0d92..46912c5 100644 --- a/src/interpreter/background_runner.rs +++ b/src/interpreter/background_runner.rs @@ -7,12 +7,12 @@ use std::time::Duration; /// It is OK to suppress unused warning for this code, since it join the thread in drop() /// correctly, example: /// -/// ```rust -/// pub struct Context { +/// ``rust +/// pub struct SomeView { /// #[allow(unused)] /// bg_runner: BackgroundRunner, /// } -/// ``` +/// `` /// pub struct BackgroundRunner { interval: Duration, diff --git a/src/interpreter/context.rs b/src/interpreter/context.rs index e798f1b..6810836 100644 --- a/src/interpreter/context.rs +++ b/src/interpreter/context.rs @@ -1,6 +1,6 @@ +use crate::actions::ActionDescription; use crate::interpreter::{options::ChDigOptions, ClickHouse, Worker}; use anyhow::Result; -use chdig::ActionDescription; use chrono::Duration; use cursive::{event::Event, event::EventResult, views::Dialog, views::OnEventView, Cursive, View}; use std::sync::{atomic, Arc, Condvar, Mutex}; diff --git a/src/interpreter/worker.rs b/src/interpreter/worker.rs index c9ff67a..d23c072 100644 --- a/src/interpreter/worker.rs +++ b/src/interpreter/worker.rs @@ -2,10 +2,10 @@ use crate::{ common::Stopwatch, interpreter::clickhouse::{Columns, TraceType}, interpreter::{flamegraph, ContextArc}, + utils::{highlight_sql, open_graph_in_browser}, view::{self, Navigation}, }; use anyhow::{anyhow, Result}; -use chdig::{highlight_sql, open_graph_in_browser}; use chrono::{DateTime, Local}; // FIXME: "leaky abstractions" use clickhouse_rs::errors::Error as ClickHouseError; diff --git a/src/lib.rs b/src/lib.rs index d26612d..21994a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,8 @@ mod actions; +mod common; +mod interpreter; mod utils; +mod view; -// utils -pub use utils::edit_query; -#[cfg(not(target_family = "windows"))] -pub use utils::fuzzy_actions; -pub use utils::get_query; -pub use utils::highlight_sql; -pub use utils::open_graph_in_browser; - -// actions -pub use actions::ActionDescription; +mod chdig; +pub use chdig::chdig_main; diff --git a/src/main.rs b/src/main.rs index 35482dc..ef22964 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,83 +1,7 @@ use anyhow::Result; -use backtrace::Backtrace; -use flexi_logger::{LogSpecification, Logger}; -use std::panic::{self, PanicHookInfo}; -use std::sync::Arc; - -mod common; -mod interpreter; -mod view; - -use crate::{ - interpreter::{options, ClickHouse, Context, ContextArc}, - view::Navigation, -}; - -fn panic_hook(info: &PanicHookInfo<'_>) { - let location = info.location().unwrap(); - - let msg = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &s[..], - None => "Box", - }, - }; - - // NOTE: we need to add \r since the terminal is in raw mode. - // (another option is to restore the terminal state with termios) - let stacktrace: String = format!("{:?}", Backtrace::new()).replace('\n', "\n\r"); - - print!( - "\n\rthread '' panicked at '{}', {}\n\r{}", - msg, location, stacktrace - ); -} +use chdig::chdig_main; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { - let options = options::parse()?; - - // Initialize it before any backends (otherwise backend will prepare terminal for TUI app, and - // panic hook will clear the screen). - let clickhouse = Arc::new(ClickHouse::new(options.clickhouse.clone()).await?); - - panic::set_hook(Box::new(|info| { - panic_hook(info); - })); - - #[cfg(not(target_family = "windows"))] - let backend = cursive::backends::termion::Backend::init()?; - #[cfg(target_family = "windows")] - let backend = cursive::backends::crossterm::Backend::init()?; - - let buffered_backend = Box::new(cursive_buffered_backend::BufferedBackend::new(backend)); - let mut siv = cursive::CursiveRunner::new(cursive::Cursive::new(), buffered_backend); - - // Override with RUST_LOG - // - // NOTE: hyper also has trace_span() which will not be overwritten - // - // FIXME: should be initialize before options, but options prints completion that should be - // done before terminal switched to raw mode. - let logger = Logger::try_with_env_or_str( - "trace,cursive=info,clickhouse_rs=info,skim=info,tuikit=info,hyper=info,rustls=info", - )? - .log_to_writer(cursive_flexi_logger_view::cursive_flexi_logger(&siv)) - .format(flexi_logger::colored_with_thread) - .start()?; - - // FIXME: should be initialized before cursive, otherwise on error it clears the terminal. - let context: ContextArc = Context::new(options, clickhouse, siv.cb_sink().clone()).await?; - - siv.chdig(context.clone()); - - log::info!("chdig started"); - siv.run(); - - // Suppress error from the cursive_flexi_logger_view - "cursive callback sink is closed!" - // Note, cursive_flexi_logger_view does not implements shutdown() so it will not help. - logger.set_new_spec(LogSpecification::parse("none").unwrap()); - - return Ok(()); + return chdig_main().await; } diff --git a/src/utils.rs b/src/utils.rs index dfa9365..28dae32 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,7 +10,7 @@ use tempfile::Builder; use urlencoding::encode; #[cfg(not(target_family = "windows"))] -use {crate::ActionDescription, skim::prelude::*}; +use {crate::actions::ActionDescription, skim::prelude::*}; #[cfg(not(target_family = "windows"))] impl SkimItem for ActionDescription { diff --git a/src/view/navigation.rs b/src/view/navigation.rs index e2285e0..bbf15ae 100644 --- a/src/view/navigation.rs +++ b/src/view/navigation.rs @@ -1,3 +1,5 @@ +#[cfg(not(target_family = "windows"))] +use crate::utils::fuzzy_actions; use crate::{ interpreter::{ clickhouse::TraceType, @@ -7,8 +9,6 @@ use crate::{ view::{self, TextLogView}, }; use anyhow::Result; -#[cfg(not(target_family = "windows"))] -use chdig::fuzzy_actions; use cursive::{ event::{Event, EventResult, Key}, theme::{BaseColor, Color, ColorStyle, Effect, PaletteColor, Style, Theme}, diff --git a/src/view/processes_view.rs b/src/view/processes_view.rs index 018f2ff..61a7425 100644 --- a/src/view/processes_view.rs +++ b/src/view/processes_view.rs @@ -16,13 +16,15 @@ use cursive::{ }; use size::{Base, SizeFormatter, Style}; -use crate::interpreter::{ - clickhouse::Columns, clickhouse::TraceType, options::ViewOptions, BackgroundRunner, ContextArc, - QueryProcess, WorkerEvent, +use crate::{ + interpreter::{ + clickhouse::Columns, clickhouse::TraceType, options::ViewOptions, BackgroundRunner, + ContextArc, QueryProcess, WorkerEvent, + }, + utils::{edit_query, get_query}, + view::{ExtTableView, ProcessView, QueryResultView, TableViewItem, TextLogView}, + wrap_impl_no_move, }; -use crate::view::{ExtTableView, ProcessView, QueryResultView, TableViewItem, TextLogView}; -use crate::wrap_impl_no_move; -use chdig::{edit_query, get_query}; // Analog of mapFromArrays() in ClickHouse fn map_from_arrays(keys: Vec, values: Vec) -> HashMap