diff --git a/Cargo.lock b/Cargo.lock index 38629b9..238d1fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,9 +574,7 @@ dependencies = [ "rustyline", "serde", "serde_json", - "strip-ansi-escapes", "toml", - "tracing", "tree-sitter", "wasm-bindgen-futures", ] @@ -2208,15 +2206,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" -[[package]] -name = "strip-ansi-escapes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" -dependencies = [ - "vte", -] - [[package]] name = "strsim" version = "0.10.0" @@ -2421,21 +2410,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.43", -] - [[package]] name = "tracing-core" version = "0.1.32" @@ -2535,26 +2512,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vte" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" -dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "walkdir" version = "2.4.0" diff --git a/Cargo.toml b/Cargo.toml index e8b8195..2d2cd33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,7 @@ reqwest = { version = "0.11.20", features = ["blocking", "json"] } rustyline = { version = "12.0.0", features = ["derive", "with-file-history"] } serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" -strip-ansi-escapes = "0.2.0" toml = "0.8.1" -tracing = "0.1.40" tree-sitter = "0.20.10" egui = "0.24.1" eframe = { version = "0.24.1", default-features = false, features = [ diff --git a/src/bin/tui/action.rs b/src/bin/tui/action.rs deleted file mode 100644 index 051af6a..0000000 --- a/src/bin/tui/action.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::{fmt, string::ToString}; - -use serde::{ - de::{self, Deserializer, Visitor}, - Deserialize, Serialize, -}; -use strum::Display; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Display, Deserialize)] -pub enum Action { - Tick, - Render, - ReadFile, - Resize(u16, u16), - Suspend, - Resume, - Quit, - Refresh, - Error(String), - Help, - Up, - Down, - Left, - Right, -} diff --git a/src/bin/tui/app.rs b/src/bin/tui/app.rs deleted file mode 100644 index 36c3058..0000000 --- a/src/bin/tui/app.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::path::PathBuf; - -use color_eyre::eyre::Result; -use crossterm::event::KeyEvent; -use ratatui::prelude::Rect; -use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; - -use crate::{ - action::Action, - components::{fps::FpsCounter, home::Home, Component}, - config::Config, - mode::Mode, - tui, -}; - -pub struct App { - pub config: Config, - pub components: Vec>, - pub should_quit: bool, - pub should_suspend: bool, - pub mode: Mode, - pub last_tick_key_events: Vec, -} - -impl App { - pub fn new(path: PathBuf) -> Result { - let home = Home::new(path)?; - let config = Config::new()?; - let mode = Mode::Home; - Ok(Self { - components: vec![Box::new(home)], - should_quit: false, - should_suspend: false, - config, - mode, - last_tick_key_events: Vec::new(), - }) - } - - pub async fn run(&mut self) -> Result<()> { - let (action_tx, mut action_rx) = mpsc::unbounded_channel(); - - let mut tui = tui::Tui::new()?.mouse(true); - - tui.enter()?; - - for component in self.components.iter_mut() { - component.register_action_handler(action_tx.clone())?; - } - - for component in self.components.iter_mut() { - component.register_config_handler(self.config.clone())?; - } - - for component in self.components.iter_mut() { - component.init(tui.size()?)?; - } - - loop { - if let Some(e) = tui.next().await { - match e { - tui::Event::Quit => action_tx.send(Action::Quit)?, - tui::Event::Tick => action_tx.send(Action::Tick)?, - tui::Event::Render => action_tx.send(Action::Render)?, - tui::Event::Resize(x, y) => action_tx.send(Action::Resize(x, y))?, - tui::Event::Key(key) => { - if let Some(keymap) = self.config.keybindings.get(&self.mode) { - if let Some(action) = keymap.get(&vec![key]) { - log::info!("Got action: {action:?}"); - action_tx.send(action.clone())?; - } else { - // If the key was not handled as a single key action, - // then consider it for multi-key combinations. - self.last_tick_key_events.push(key); - - // Check for multi-key combinations - if let Some(action) = keymap.get(&self.last_tick_key_events) { - log::info!("Got action: {action:?}"); - action_tx.send(action.clone())?; - } - } - }; - } - _ => {} - } - for component in self.components.iter_mut() { - if let Some(action) = component.handle_events(Some(e.clone()))? { - action_tx.send(action)?; - } - } - } - - while let Ok(action) = action_rx.try_recv() { - if action != Action::Tick && action != Action::Render { - log::debug!("{action:?}"); - } - match action { - Action::Tick => { - self.last_tick_key_events.drain(..); - } - Action::Quit => self.should_quit = true, - Action::Suspend => self.should_suspend = true, - Action::Resume => self.should_suspend = false, - Action::Resize(w, h) => { - tui.resize(Rect::new(0, 0, w, h))?; - tui.draw(|f| { - for component in self.components.iter_mut() { - let r = component.draw(f, f.size()); - if let Err(e) = r { - action_tx - .send(Action::Error(format!("Failed to draw: {:?}", e))) - .unwrap(); - } - } - })?; - } - Action::Render => { - tui.draw(|f| { - for component in self.components.iter_mut() { - let r = component.draw(f, f.size()); - if let Err(e) = r { - action_tx - .send(Action::Error(format!("Failed to draw: {:?}", e))) - .unwrap(); - } - } - })?; - } - _ => {} - } - for component in self.components.iter_mut() { - if let Some(action) = component.update(action.clone())? { - action_tx.send(action)? - }; - } - } - if self.should_suspend { - tui.suspend()?; - action_tx.send(Action::Resume)?; - tui = tui::Tui::new()?; - // tui.mouse(true); - tui.enter()?; - } else if self.should_quit { - tui.stop()?; - break; - } - } - tui.exit()?; - Ok(()) - } -} diff --git a/src/bin/tui/cli.rs b/src/bin/tui/cli.rs deleted file mode 100644 index 1a54c29..0000000 --- a/src/bin/tui/cli.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::path::PathBuf; - -use clap::Parser; - -use crate::utils::version; - -#[derive(Parser, Debug)] -#[command(author, version = version(), about)] -pub struct Cli { - /// File that will be run and watched for changes. - pub path: PathBuf, -} diff --git a/src/bin/tui/components.rs b/src/bin/tui/components.rs deleted file mode 100644 index 2fbb868..0000000 --- a/src/bin/tui/components.rs +++ /dev/null @@ -1,126 +0,0 @@ -use color_eyre::eyre::Result; -use crossterm::event::{KeyEvent, MouseEvent}; -use ratatui::layout::Rect; -use tokio::sync::mpsc::UnboundedSender; - -use crate::{ - action::Action, - config::Config, - tui::{Event, Frame}, -}; - -pub mod fps; -pub mod home; -mod list_display; -mod map_display; - -/// `Component` is a trait that represents a visual and interactive element of the user interface. -/// Implementors of this trait can be registered with the main application loop and will be able to receive events, -/// update state, and be rendered on the screen. -pub trait Component { - /// Register an action handler that can send actions for processing if necessary. - /// - /// # Arguments - /// - /// * `tx` - An unbounded sender that can send actions. - /// - /// # Returns - /// - /// * `Result<()>` - An Ok result or an error. - #[allow(unused_variables)] - fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { - Ok(()) - } - /// Register a configuration handler that provides configuration settings if necessary. - /// - /// # Arguments - /// - /// * `config` - Configuration settings. - /// - /// # Returns - /// - /// * `Result<()>` - An Ok result or an error. - #[allow(unused_variables)] - fn register_config_handler(&mut self, config: Config) -> Result<()> { - Ok(()) - } - /// Initialize the component with a specified area if necessary. - /// - /// # Arguments - /// - /// * `area` - Rectangular area to initialize the component within. - /// - /// # Returns - /// - /// * `Result<()>` - An Ok result or an error. - fn init(&mut self, area: Rect) -> Result<()> { - Ok(()) - } - /// Handle incoming events and produce actions if necessary. - /// - /// # Arguments - /// - /// * `event` - An optional event to be processed. - /// - /// # Returns - /// - /// * `Result>` - An action to be processed or none. - fn handle_events(&mut self, event: Option) -> Result> { - let r = match event { - Some(Event::Key(key_event)) => self.handle_key_events(key_event)?, - Some(Event::Mouse(mouse_event)) => self.handle_mouse_events(mouse_event)?, - _ => None, - }; - Ok(r) - } - /// Handle key events and produce actions if necessary. - /// - /// # Arguments - /// - /// * `key` - A key event to be processed. - /// - /// # Returns - /// - /// * `Result>` - An action to be processed or none. - #[allow(unused_variables)] - fn handle_key_events(&mut self, key: KeyEvent) -> Result> { - Ok(None) - } - /// Handle mouse events and produce actions if necessary. - /// - /// # Arguments - /// - /// * `mouse` - A mouse event to be processed. - /// - /// # Returns - /// - /// * `Result>` - An action to be processed or none. - #[allow(unused_variables)] - fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result> { - Ok(None) - } - /// Update the state of the component based on a received action. (REQUIRED) - /// - /// # Arguments - /// - /// * `action` - An action that may modify the state of the component. - /// - /// # Returns - /// - /// * `Result>` - An action to be processed or none. - #[allow(unused_variables)] - fn update(&mut self, action: Action) -> Result> { - Ok(None) - } - /// Render the component on the screen. (REQUIRED) - /// - /// # Arguments - /// - /// * `f` - A frame used for rendering. - /// * `area` - The area in which the component should be drawn. - /// - /// # Returns - /// - /// * `Result<()>` - An Ok result or an error. - fn draw(&mut self, f: &mut Frame<'_>, area: Rect) -> Result<()>; -} diff --git a/src/bin/tui/components/fps.rs b/src/bin/tui/components/fps.rs deleted file mode 100644 index 7c79805..0000000 --- a/src/bin/tui/components/fps.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::time::Instant; - -use color_eyre::eyre::Result; -use ratatui::{prelude::*, widgets::*}; - -use super::Component; -use crate::{action::Action, tui::Frame}; - -#[derive(Debug, Clone, PartialEq)] -pub struct FpsCounter { - app_start_time: Instant, - app_frames: u32, - app_fps: f64, - - render_start_time: Instant, - render_frames: u32, - render_fps: f64, -} - -impl Default for FpsCounter { - fn default() -> Self { - Self::new() - } -} - -impl FpsCounter { - pub fn new() -> Self { - Self { - app_start_time: Instant::now(), - app_frames: 0, - app_fps: 0.0, - render_start_time: Instant::now(), - render_frames: 0, - render_fps: 0.0, - } - } - - fn app_tick(&mut self) -> Result<()> { - self.app_frames += 1; - let now = Instant::now(); - let elapsed = (now - self.app_start_time).as_secs_f64(); - if elapsed >= 1.0 { - self.app_fps = self.app_frames as f64 / elapsed; - self.app_start_time = now; - self.app_frames = 0; - } - Ok(()) - } - - fn render_tick(&mut self) -> Result<()> { - self.render_frames += 1; - let now = Instant::now(); - let elapsed = (now - self.render_start_time).as_secs_f64(); - if elapsed >= 1.0 { - self.render_fps = self.render_frames as f64 / elapsed; - self.render_start_time = now; - self.render_frames = 0; - } - Ok(()) - } -} - -impl Component for FpsCounter { - fn update(&mut self, action: Action) -> Result> { - if let Action::Tick = action { - self.app_tick()? - }; - if let Action::Render = action { - self.render_tick()? - }; - Ok(None) - } - - fn draw(&mut self, f: &mut Frame<'_>, rect: Rect) -> Result<()> { - let rects = Layout::default() - .direction(Direction::Vertical) - .constraints(vec![ - Constraint::Length(1), // first row - Constraint::Min(0), - ]) - .split(rect); - - let rect = rects[0]; - - let s = format!("{:.2} ticks per sec (app) {:.2} frames per sec (render)", self.app_fps, self.render_fps); - let block = Block::default().title(block::Title::from(s.dim()).alignment(Alignment::Right)); - f.render_widget(block, rect); - Ok(()) - } -} diff --git a/src/bin/tui/components/home.rs b/src/bin/tui/components/home.rs deleted file mode 100644 index 082fee7..0000000 --- a/src/bin/tui/components/home.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::{ - collections::HashMap, - fs::read_to_string, - path::PathBuf, - time::{Duration, SystemTime}, -}; - -use color_eyre::eyre::Result; -use crossterm::event::{KeyCode, KeyEvent}; -use dust_lang::{interpret, interpret_with_context, Map, Value}; -use ratatui::{prelude::*, widgets::*}; -use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc::UnboundedSender; -use tui_tree_widget::{Tree, TreeItem, TreeState}; - -use super::{map_display::MapDisplay, Component, Frame}; -use crate::{ - action::Action, - config::{Config, KeyBindings}, -}; - -pub struct Home<'a> { - command_tx: Option>, - config: Config, - path: PathBuf, - source: String, - context: Map, - context_tree_state: TreeState, - context_display: MapDisplay<'a>, - output: dust_lang::Result, - last_modified: SystemTime, -} - -impl<'a> Home<'a> { - pub fn new(path: PathBuf) -> Result { - let context = Map::new(); - let context_display = MapDisplay::new(context.clone())?; - - Ok(Home { - command_tx: None, - config: Config::default(), - path, - source: String::new(), - context, - context_display, - context_tree_state: TreeState::default(), - output: Ok(Value::default()), - last_modified: SystemTime::now(), - }) - } -} - -impl<'a> Component for Home<'a> { - fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { - self.command_tx = Some(tx); - Ok(()) - } - - fn register_config_handler(&mut self, config: Config) -> Result<()> { - self.config = config; - Ok(()) - } - - fn update(&mut self, action: Action) -> Result> { - match action { - Action::Tick => { - let modified = self.path.metadata()?.modified()?; - - if modified != self.last_modified { - self.source = read_to_string(&self.path)?; - self.last_modified = modified; - self.output = interpret_with_context(&self.source, self.context.clone()); - self.context_display = MapDisplay::new(self.context.clone())?; - } - } - _ => {} - } - - self.context_display.update(action) - } - - fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let layout = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) - .split(area); - - self.context_display.draw(frame, layout[0])?; - - let output_text = match &self.output { - Ok(value) => value.to_string(), - Err(error) => error.to_string(), - }; - - frame.render_widget(Paragraph::new(output_text), layout[1]); - - Ok(()) - } -} diff --git a/src/bin/tui/components/list_display.rs b/src/bin/tui/components/list_display.rs deleted file mode 100644 index 29c3592..0000000 --- a/src/bin/tui/components/list_display.rs +++ /dev/null @@ -1,5 +0,0 @@ -use dust_lang::List; - -pub struct ListDisplay { - list: List, -} diff --git a/src/bin/tui/components/map_display.rs b/src/bin/tui/components/map_display.rs deleted file mode 100644 index 8024438..0000000 --- a/src/bin/tui/components/map_display.rs +++ /dev/null @@ -1,106 +0,0 @@ -use color_eyre::Result; -use dust_lang::{Map, Type, Value}; -use lazy_static::lazy_static; -use ratatui::{ - prelude::Rect, - style::{Color, Modifier, Style, Stylize}, - widgets::{Block, Borders}, - Frame, -}; -use serial_int::SerialGenerator; -use std::{hash::Hash, sync::Mutex}; -use tui_tree_widget::{Tree, TreeItem, TreeState}; - -use crate::{action::Action, components::Component}; - -lazy_static! { - static ref ID_GENERATOR: Mutex> = Mutex::new(SerialGenerator::new()); -} - -fn create_tree_item<'a>(key: String, value: &Value) -> Result> { - let tree_item = match value { - Value::List(list) => { - let mut items = Vec::new(); - - for (index, value) in list.items().iter().enumerate() { - let item = create_tree_item(index.to_string(), value)?; - - items.push(item); - } - - TreeItem::new(ID_GENERATOR.lock().unwrap().generate(), key, items)? - } - Value::Map(map) => { - let mut items = Vec::new(); - - for (key, (value, _)) in map.variables()?.iter() { - let item = create_tree_item(key.to_string(), value)?; - - items.push(item); - } - - TreeItem::new(ID_GENERATOR.lock().unwrap().generate(), key, items)? - } - Value::Function(_) => todo!(), - Value::String(string) => TreeItem::new_leaf( - ID_GENERATOR.lock().unwrap().generate(), - format!("{key} = {value}"), - ), - Value::Float(float) => TreeItem::new_leaf( - ID_GENERATOR.lock().unwrap().generate(), - format!("{key} = {value}"), - ), - Value::Integer(integer) => TreeItem::new_leaf( - ID_GENERATOR.lock().unwrap().generate(), - format!("{key} = {value}"), - ), - Value::Boolean(_) => todo!(), - Value::Option(_) => todo!(), - }; - - Ok(tree_item) -} - -pub struct MapDisplay<'a> { - tree_state: TreeState, - items: Vec>, -} - -impl<'a> MapDisplay<'a> { - pub fn new(map: Map) -> Result { - let tree_state = TreeState::default(); - let mut items = Vec::new(); - - for (key, (value, _)) in map.variables()?.iter() { - let item = create_tree_item(key.to_string(), value)?; - - items.push(item); - } - - Ok(MapDisplay { tree_state, items }) - } -} - -impl<'a> Component for MapDisplay<'a> { - fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let tree = Tree::new(self.items.clone())? - .block(Block::new().title("context").borders(Borders::ALL)) - .highlight_style(Style::new().add_modifier(Modifier::BOLD)); - - frame.render_stateful_widget(tree, area, &mut self.tree_state); - - Ok(()) - } - - fn update(&mut self, action: Action) -> Result> { - match action { - Action::Up => self.tree_state.key_up(self.items.as_slice()), - Action::Down => self.tree_state.key_down(&self.items), - Action::Left => self.tree_state.key_left(), - Action::Right => self.tree_state.key_right(), - _ => {} - } - - Ok(None) - } -} diff --git a/src/bin/tui/config.rs b/src/bin/tui/config.rs deleted file mode 100644 index 44d91d9..0000000 --- a/src/bin/tui/config.rs +++ /dev/null @@ -1,563 +0,0 @@ -use std::{collections::HashMap, fmt, path::PathBuf}; - -use color_eyre::eyre::Result; -use config::Value; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use derive_deref::{Deref, DerefMut}; -use ratatui::style::{Color, Modifier, Style}; -use serde::{ - de::{self, Deserializer, MapAccess, Visitor}, - Deserialize, Serialize, -}; -use serde_json::Value as JsonValue; - -use crate::{action::Action, mode::Mode}; - -const CONFIG: &str = include_str!("../../../assets/config.json5"); - -#[derive(Clone, Debug, Deserialize, Default)] -pub struct AppConfig { - #[serde(default)] - pub _data_dir: PathBuf, - #[serde(default)] - pub _config_dir: PathBuf, -} - -#[derive(Clone, Debug, Default, Deserialize)] -pub struct Config { - #[serde(default, flatten)] - pub config: AppConfig, - #[serde(default)] - pub keybindings: KeyBindings, - #[serde(default)] - pub styles: Styles, -} - -impl Config { - pub fn new() -> Result { - let default_config: Config = json5::from_str(CONFIG).unwrap(); - let data_dir = crate::utils::get_data_dir(); - let config_dir = crate::utils::get_config_dir(); - let mut builder = config::Config::builder() - .set_default("_data_dir", data_dir.to_str().unwrap())? - .set_default("_config_dir", config_dir.to_str().unwrap())?; - - let config_files = [ - ("config.json5", config::FileFormat::Json5), - ("config.json", config::FileFormat::Json), - ("config.yaml", config::FileFormat::Yaml), - ("config.toml", config::FileFormat::Toml), - ("config.ini", config::FileFormat::Ini), - ]; - let mut found_config = false; - for (file, format) in &config_files { - builder = builder.add_source( - config::File::from(config_dir.join(file)) - .format(*format) - .required(false), - ); - if config_dir.join(file).exists() { - found_config = true - } - } - if !found_config { - log::error!("No configuration file found. Application may not behave as expected"); - } - - let mut cfg: Self = builder.build()?.try_deserialize()?; - - for (mode, default_bindings) in default_config.keybindings.iter() { - let user_bindings = cfg.keybindings.entry(*mode).or_default(); - for (key, cmd) in default_bindings.iter() { - user_bindings - .entry(key.clone()) - .or_insert_with(|| cmd.clone()); - } - } - for (mode, default_styles) in default_config.styles.iter() { - let user_styles = cfg.styles.entry(*mode).or_default(); - for (style_key, style) in default_styles.iter() { - user_styles - .entry(style_key.clone()) - .or_insert_with(|| style.clone()); - } - } - - Ok(cfg) - } -} - -#[derive(Clone, Debug, Default, Deref, DerefMut)] -pub struct KeyBindings(pub HashMap, Action>>); - -impl<'de> Deserialize<'de> for KeyBindings { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let parsed_map = HashMap::>::deserialize(deserializer)?; - - let keybindings = parsed_map - .into_iter() - .map(|(mode, inner_map)| { - let converted_inner_map = inner_map - .into_iter() - .map(|(key_str, cmd)| (parse_key_sequence(&key_str).unwrap(), cmd)) - .collect(); - (mode, converted_inner_map) - }) - .collect(); - - Ok(KeyBindings(keybindings)) - } -} - -fn parse_key_event(raw: &str) -> Result { - let raw_lower = raw.to_ascii_lowercase(); - let (remaining, modifiers) = extract_modifiers(&raw_lower); - parse_key_code_with_modifiers(remaining, modifiers) -} - -fn extract_modifiers(raw: &str) -> (&str, KeyModifiers) { - let mut modifiers = KeyModifiers::empty(); - let mut current = raw; - - loop { - match current { - rest if rest.starts_with("ctrl-") => { - modifiers.insert(KeyModifiers::CONTROL); - current = &rest[5..]; - } - rest if rest.starts_with("alt-") => { - modifiers.insert(KeyModifiers::ALT); - current = &rest[4..]; - } - rest if rest.starts_with("shift-") => { - modifiers.insert(KeyModifiers::SHIFT); - current = &rest[6..]; - } - _ => break, // break out of the loop if no known prefix is detected - }; - } - - (current, modifiers) -} - -fn parse_key_code_with_modifiers( - raw: &str, - mut modifiers: KeyModifiers, -) -> Result { - let c = match raw { - "esc" => KeyCode::Esc, - "enter" => KeyCode::Enter, - "left" => KeyCode::Left, - "right" => KeyCode::Right, - "up" => KeyCode::Up, - "down" => KeyCode::Down, - "home" => KeyCode::Home, - "end" => KeyCode::End, - "pageup" => KeyCode::PageUp, - "pagedown" => KeyCode::PageDown, - "backtab" => { - modifiers.insert(KeyModifiers::SHIFT); - KeyCode::BackTab - } - "backspace" => KeyCode::Backspace, - "delete" => KeyCode::Delete, - "insert" => KeyCode::Insert, - "f1" => KeyCode::F(1), - "f2" => KeyCode::F(2), - "f3" => KeyCode::F(3), - "f4" => KeyCode::F(4), - "f5" => KeyCode::F(5), - "f6" => KeyCode::F(6), - "f7" => KeyCode::F(7), - "f8" => KeyCode::F(8), - "f9" => KeyCode::F(9), - "f10" => KeyCode::F(10), - "f11" => KeyCode::F(11), - "f12" => KeyCode::F(12), - "space" => KeyCode::Char(' '), - "hyphen" => KeyCode::Char('-'), - "minus" => KeyCode::Char('-'), - "tab" => KeyCode::Tab, - c if c.len() == 1 => { - let mut c = c.chars().next().unwrap(); - if modifiers.contains(KeyModifiers::SHIFT) { - c = c.to_ascii_uppercase(); - } - KeyCode::Char(c) - } - _ => return Err(format!("Unable to parse {raw}")), - }; - Ok(KeyEvent::new(c, modifiers)) -} - -pub fn key_event_to_string(key_event: &KeyEvent) -> String { - let char; - let key_code = match key_event.code { - KeyCode::Backspace => "backspace", - KeyCode::Enter => "enter", - KeyCode::Left => "left", - KeyCode::Right => "right", - KeyCode::Up => "up", - KeyCode::Down => "down", - KeyCode::Home => "home", - KeyCode::End => "end", - KeyCode::PageUp => "pageup", - KeyCode::PageDown => "pagedown", - KeyCode::Tab => "tab", - KeyCode::BackTab => "backtab", - KeyCode::Delete => "delete", - KeyCode::Insert => "insert", - KeyCode::F(c) => { - char = format!("f({c})"); - &char - } - KeyCode::Char(c) if c == ' ' => "space", - KeyCode::Char(c) => { - char = c.to_string(); - &char - } - KeyCode::Esc => "esc", - KeyCode::Null => "", - KeyCode::CapsLock => "", - KeyCode::Menu => "", - KeyCode::ScrollLock => "", - KeyCode::Media(_) => "", - KeyCode::NumLock => "", - KeyCode::PrintScreen => "", - KeyCode::Pause => "", - KeyCode::KeypadBegin => "", - KeyCode::Modifier(_) => "", - }; - - let mut modifiers = Vec::with_capacity(3); - - if key_event.modifiers.intersects(KeyModifiers::CONTROL) { - modifiers.push("ctrl"); - } - - if key_event.modifiers.intersects(KeyModifiers::SHIFT) { - modifiers.push("shift"); - } - - if key_event.modifiers.intersects(KeyModifiers::ALT) { - modifiers.push("alt"); - } - - let mut key = modifiers.join("-"); - - if !key.is_empty() { - key.push('-'); - } - key.push_str(key_code); - - key -} - -pub fn parse_key_sequence(raw: &str) -> Result, String> { - if raw.chars().filter(|c| *c == '>').count() != raw.chars().filter(|c| *c == '<').count() { - return Err(format!("Unable to parse `{}`", raw)); - } - let raw = if !raw.contains("><") { - let raw = raw.strip_prefix('<').unwrap_or(raw); - let raw = raw.strip_prefix('>').unwrap_or(raw); - raw - } else { - raw - }; - let sequences = raw - .split("><") - .map(|seq| { - if let Some(s) = seq.strip_prefix('<') { - s - } else if let Some(s) = seq.strip_suffix('>') { - s - } else { - seq - } - }) - .collect::>(); - - sequences.into_iter().map(parse_key_event).collect() -} - -#[derive(Clone, Debug, Default, Deref, DerefMut)] -pub struct Styles(pub HashMap>); - -impl<'de> Deserialize<'de> for Styles { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let parsed_map = HashMap::>::deserialize(deserializer)?; - - let styles = parsed_map - .into_iter() - .map(|(mode, inner_map)| { - let converted_inner_map = inner_map - .into_iter() - .map(|(str, style)| (str, parse_style(&style))) - .collect(); - (mode, converted_inner_map) - }) - .collect(); - - Ok(Styles(styles)) - } -} - -pub fn parse_style(line: &str) -> Style { - let (foreground, background) = - line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len())); - let foreground = process_color_string(foreground); - let background = process_color_string(&background.replace("on ", "")); - - let mut style = Style::default(); - if let Some(fg) = parse_color(&foreground.0) { - style = style.fg(fg); - } - if let Some(bg) = parse_color(&background.0) { - style = style.bg(bg); - } - style = style.add_modifier(foreground.1 | background.1); - style -} - -fn process_color_string(color_str: &str) -> (String, Modifier) { - let color = color_str - .replace("grey", "gray") - .replace("bright ", "") - .replace("bold ", "") - .replace("underline ", "") - .replace("inverse ", ""); - - let mut modifiers = Modifier::empty(); - if color_str.contains("underline") { - modifiers |= Modifier::UNDERLINED; - } - if color_str.contains("bold") { - modifiers |= Modifier::BOLD; - } - if color_str.contains("inverse") { - modifiers |= Modifier::REVERSED; - } - - (color, modifiers) -} - -fn parse_color(s: &str) -> Option { - let s = s.trim_start(); - let s = s.trim_end(); - if s.contains("bright color") { - let s = s.trim_start_matches("bright "); - let c = s - .trim_start_matches("color") - .parse::() - .unwrap_or_default(); - Some(Color::Indexed(c.wrapping_shl(8))) - } else if s.contains("color") { - let c = s - .trim_start_matches("color") - .parse::() - .unwrap_or_default(); - Some(Color::Indexed(c)) - } else if s.contains("gray") { - let c = 232 - + s.trim_start_matches("gray") - .parse::() - .unwrap_or_default(); - Some(Color::Indexed(c)) - } else if s.contains("rgb") { - let red = (s.as_bytes()[3] as char).to_digit(10).unwrap_or_default() as u8; - let green = (s.as_bytes()[4] as char).to_digit(10).unwrap_or_default() as u8; - let blue = (s.as_bytes()[5] as char).to_digit(10).unwrap_or_default() as u8; - let c = 16 + red * 36 + green * 6 + blue; - Some(Color::Indexed(c)) - } else if s == "bold black" { - Some(Color::Indexed(8)) - } else if s == "bold red" { - Some(Color::Indexed(9)) - } else if s == "bold green" { - Some(Color::Indexed(10)) - } else if s == "bold yellow" { - Some(Color::Indexed(11)) - } else if s == "bold blue" { - Some(Color::Indexed(12)) - } else if s == "bold magenta" { - Some(Color::Indexed(13)) - } else if s == "bold cyan" { - Some(Color::Indexed(14)) - } else if s == "bold white" { - Some(Color::Indexed(15)) - } else if s == "black" { - Some(Color::Indexed(0)) - } else if s == "red" { - Some(Color::Indexed(1)) - } else if s == "green" { - Some(Color::Indexed(2)) - } else if s == "yellow" { - Some(Color::Indexed(3)) - } else if s == "blue" { - Some(Color::Indexed(4)) - } else if s == "magenta" { - Some(Color::Indexed(5)) - } else if s == "cyan" { - Some(Color::Indexed(6)) - } else if s == "white" { - Some(Color::Indexed(7)) - } else { - None - } -} - -#[cfg(test)] -mod tests { - use pretty_assertions::assert_eq; - - use super::*; - - #[test] - fn test_parse_style_default() { - let style = parse_style(""); - assert_eq!(style, Style::default()); - } - - #[test] - fn test_parse_style_foreground() { - let style = parse_style("red"); - assert_eq!(style.fg, Some(Color::Indexed(1))); - } - - #[test] - fn test_parse_style_background() { - let style = parse_style("on blue"); - assert_eq!(style.bg, Some(Color::Indexed(4))); - } - - #[test] - fn test_parse_style_modifiers() { - let style = parse_style("underline red on blue"); - assert_eq!(style.fg, Some(Color::Indexed(1))); - assert_eq!(style.bg, Some(Color::Indexed(4))); - } - - #[test] - fn test_process_color_string() { - let (color, modifiers) = process_color_string("underline bold inverse gray"); - assert_eq!(color, "gray"); - assert!(modifiers.contains(Modifier::UNDERLINED)); - assert!(modifiers.contains(Modifier::BOLD)); - assert!(modifiers.contains(Modifier::REVERSED)); - } - - #[test] - fn test_parse_color_rgb() { - let color = parse_color("rgb123"); - let expected = 16 + 1 * 36 + 2 * 6 + 3; - assert_eq!(color, Some(Color::Indexed(expected))); - } - - #[test] - fn test_parse_color_unknown() { - let color = parse_color("unknown"); - assert_eq!(color, None); - } - - #[test] - fn test_config() -> Result<()> { - let c = Config::new()?; - assert_eq!( - c.keybindings - .get(&Mode::Home) - .unwrap() - .get(&parse_key_sequence("").unwrap_or_default()) - .unwrap(), - &Action::Quit - ); - Ok(()) - } - - #[test] - fn test_simple_keys() { - assert_eq!( - parse_key_event("a").unwrap(), - KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty()) - ); - - assert_eq!( - parse_key_event("enter").unwrap(), - KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()) - ); - - assert_eq!( - parse_key_event("esc").unwrap(), - KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()) - ); - } - - #[test] - fn test_with_modifiers() { - assert_eq!( - parse_key_event("ctrl-a").unwrap(), - KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL) - ); - - assert_eq!( - parse_key_event("alt-enter").unwrap(), - KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT) - ); - - assert_eq!( - parse_key_event("shift-esc").unwrap(), - KeyEvent::new(KeyCode::Esc, KeyModifiers::SHIFT) - ); - } - - #[test] - fn test_multiple_modifiers() { - assert_eq!( - parse_key_event("ctrl-alt-a").unwrap(), - KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::CONTROL | KeyModifiers::ALT - ) - ); - - assert_eq!( - parse_key_event("ctrl-shift-enter").unwrap(), - KeyEvent::new(KeyCode::Enter, KeyModifiers::CONTROL | KeyModifiers::SHIFT) - ); - } - - #[test] - fn test_reverse_multiple_modifiers() { - assert_eq!( - key_event_to_string(&KeyEvent::new( - KeyCode::Char('a'), - KeyModifiers::CONTROL | KeyModifiers::ALT - )), - "ctrl-alt-a".to_string() - ); - } - - #[test] - fn test_invalid_keys() { - assert!(parse_key_event("invalid-key").is_err()); - assert!(parse_key_event("ctrl-invalid-key").is_err()); - } - - #[test] - fn test_case_insensitivity() { - assert_eq!( - parse_key_event("CTRL-a").unwrap(), - KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL) - ); - - assert_eq!( - parse_key_event("AlT-eNtEr").unwrap(), - KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT) - ); - } -} diff --git a/src/bin/tui/main.rs b/src/bin/tui/main.rs deleted file mode 100644 index a611157..0000000 --- a/src/bin/tui/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] - -pub mod action; -pub mod app; -pub mod cli; -pub mod components; -pub mod config; -pub mod mode; -pub mod tui; -pub mod utils; - -use std::env; - -use clap::Parser; -use cli::Cli; -use color_eyre::eyre::Result; - -use crate::{ - app::App, - utils::{initialize_logging, initialize_panic_handler, version}, -}; - -async fn tokio_main() -> Result<()> { - initialize_panic_handler()?; - initialize_logging()?; - - env::set_var("DUST_OUTPUT_MODE", "SILENT"); - - let args = Cli::parse(); - let mut app = App::new(args.path)?; - app.run().await?; - - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - if let Err(e) = tokio_main().await { - eprintln!("{} error: Something went wrong", env!("CARGO_PKG_NAME")); - Err(e) - } else { - Ok(()) - } -} diff --git a/src/bin/tui/mode.rs b/src/bin/tui/mode.rs deleted file mode 100644 index 1775f94..0000000 --- a/src/bin/tui/mode.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Mode { - #[default] - Home, -} diff --git a/src/bin/tui/tui.rs b/src/bin/tui/tui.rs deleted file mode 100644 index aa9783b..0000000 --- a/src/bin/tui/tui.rs +++ /dev/null @@ -1,271 +0,0 @@ -use std::{ - ops::{Deref, DerefMut}, - time::Duration, -}; - -use color_eyre::eyre::Result; -use crossterm::{ - cursor, - event::{ - DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, - Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent, - }, - terminal::{EnterAlternateScreen, LeaveAlternateScreen}, -}; -use futures::{FutureExt, StreamExt}; -use ratatui::backend::CrosstermBackend as Backend; -use serde::{Deserialize, Serialize}; -use tokio::{ - sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, - task::JoinHandle, -}; -use tokio_util::sync::CancellationToken; - -pub type IO = std::io::Stderr; -pub fn io() -> IO { - std::io::stderr() -} -pub type Frame<'a> = ratatui::Frame<'a>; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum Event { - Init, - Quit, - Error, - Closed, - Tick, - Render, - FocusGained, - FocusLost, - Paste(String), - Key(KeyEvent), - Mouse(MouseEvent), - Resize(u16, u16), -} - -pub struct Tui { - pub terminal: ratatui::Terminal>, - pub task: JoinHandle<()>, - pub cancellation_token: CancellationToken, - pub event_rx: UnboundedReceiver, - pub event_tx: UnboundedSender, - pub frame_rate: f64, - pub tick_rate: f64, - pub mouse: bool, - pub paste: bool, -} - -impl Tui { - pub fn new() -> Result { - let tick_rate = 2.0; - let frame_rate = 30.0; - let terminal = ratatui::Terminal::new(Backend::new(io()))?; - let (event_tx, event_rx) = mpsc::unbounded_channel(); - let cancellation_token = CancellationToken::new(); - let task = tokio::spawn(async {}); - let mouse = false; - let paste = false; - - Ok(Self { - terminal, - task, - cancellation_token, - event_rx, - event_tx, - frame_rate, - tick_rate, - mouse, - paste, - }) - } - - pub fn tick_rate(mut self, tick_rate: f64) -> Self { - self.tick_rate = tick_rate; - self - } - - pub fn frame_rate(mut self, frame_rate: f64) -> Self { - self.frame_rate = frame_rate; - self - } - - pub fn mouse(mut self, mouse: bool) -> Self { - self.mouse = mouse; - self - } - - pub fn paste(mut self, paste: bool) -> Self { - self.paste = paste; - self - } - - pub fn start(&mut self) { - let tick_delay = std::time::Duration::from_secs_f64(1.0 / self.tick_rate); - let render_delay = std::time::Duration::from_secs_f64(1.0 / self.frame_rate); - - self.cancel(); - self.cancellation_token = CancellationToken::new(); - - let _cancellation_token = self.cancellation_token.clone(); - let _event_tx = self.event_tx.clone(); - - self.task = tokio::spawn(async move { - let mut reader = crossterm::event::EventStream::new(); - let mut tick_interval = tokio::time::interval(tick_delay); - let mut render_interval = tokio::time::interval(render_delay); - - _event_tx.send(Event::Init).unwrap(); - - loop { - let tick_delay = tick_interval.tick(); - let render_delay = render_interval.tick(); - let crossterm_event = reader.next().fuse(); - - tokio::select! { - _ = _cancellation_token.cancelled() => { - break; - } - maybe_event = crossterm_event => { - match maybe_event { - Some(Ok(evt)) => { - match evt { - CrosstermEvent::Key(key) => { - if key.kind == KeyEventKind::Press { - _event_tx.send(Event::Key(key)).unwrap(); - } - }, - CrosstermEvent::Mouse(mouse) => { - _event_tx.send(Event::Mouse(mouse)).unwrap(); - }, - CrosstermEvent::Resize(x, y) => { - _event_tx.send(Event::Resize(x, y)).unwrap(); - }, - CrosstermEvent::FocusLost => { - _event_tx.send(Event::FocusLost).unwrap(); - }, - CrosstermEvent::FocusGained => { - _event_tx.send(Event::FocusGained).unwrap(); - }, - CrosstermEvent::Paste(s) => { - _event_tx.send(Event::Paste(s)).unwrap(); - }, - } - } - Some(Err(_)) => { - _event_tx.send(Event::Error).unwrap(); - } - None => {}, - } - }, - _ = tick_delay => { - _event_tx.send(Event::Tick).unwrap(); - }, - _ = render_delay => { - _event_tx.send(Event::Render).unwrap(); - }, - } - } - }); - } - - pub fn stop(&self) -> Result<()> { - self.cancel(); - - let mut counter = 0; - - while !self.task.is_finished() { - std::thread::sleep(Duration::from_millis(1)); - - counter += 1; - - if counter > 50 { - self.task.abort(); - } - - if counter > 100 { - log::error!("Failed to abort task in 100 milliseconds for unknown reason"); - break; - } - } - - Ok(()) - } - - pub fn enter(&mut self) -> Result<()> { - crossterm::terminal::enable_raw_mode()?; - crossterm::execute!(io(), EnterAlternateScreen, cursor::Hide)?; - - if self.mouse { - crossterm::execute!(io(), EnableMouseCapture)?; - } - - if self.paste { - crossterm::execute!(io(), EnableBracketedPaste)?; - } - - self.start(); - - Ok(()) - } - - pub fn exit(&mut self) -> Result<()> { - self.stop()?; - - if crossterm::terminal::is_raw_mode_enabled()? { - self.flush()?; - - if self.paste { - crossterm::execute!(io(), DisableBracketedPaste)?; - } - - if self.mouse { - crossterm::execute!(io(), DisableMouseCapture)?; - } - - crossterm::execute!(io(), LeaveAlternateScreen, cursor::Show)?; - crossterm::terminal::disable_raw_mode()?; - } - - Ok(()) - } - - pub fn cancel(&self) { - self.cancellation_token.cancel(); - } - - pub fn suspend(&mut self) -> Result<()> { - self.exit()?; - #[cfg(not(windows))] - signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP)?; - Ok(()) - } - - pub fn resume(&mut self) -> Result<()> { - self.enter()?; - Ok(()) - } - - pub async fn next(&mut self) -> Option { - self.event_rx.recv().await - } -} - -impl Deref for Tui { - type Target = ratatui::Terminal>; - - fn deref(&self) -> &Self::Target { - &self.terminal - } -} - -impl DerefMut for Tui { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.terminal - } -} - -impl Drop for Tui { - fn drop(&mut self) { - self.exit().unwrap(); - } -} diff --git a/src/bin/tui/utils.rs b/src/bin/tui/utils.rs deleted file mode 100644 index f00e99e..0000000 --- a/src/bin/tui/utils.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::path::PathBuf; - -use color_eyre::eyre::Result; -use directories::ProjectDirs; -use lazy_static::lazy_static; -use tracing::error; -use tracing_error::ErrorLayer; -use tracing_subscriber::{ - self, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, -}; - -lazy_static! { - pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase().to_string(); - pub static ref DATA_FOLDER: Option = - std::env::var(format!("{}_DATA", PROJECT_NAME.clone())) - .ok() - .map(PathBuf::from); - pub static ref CONFIG_FOLDER: Option = - std::env::var(format!("{}_CONFIG", PROJECT_NAME.clone())) - .ok() - .map(PathBuf::from); - pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone()); - pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME")); -} - -fn project_directory() -> Option { - ProjectDirs::from("com", "kdheepak", env!("CARGO_PKG_NAME")) -} - -pub fn initialize_panic_handler() -> Result<()> { - let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default() - .panic_section(format!( - "This is a bug. Consider reporting it at {}", - env!("CARGO_PKG_REPOSITORY") - )) - .capture_span_trace_by_default(false) - .display_location_section(false) - .display_env_section(false) - .into_hooks(); - eyre_hook.install()?; - std::panic::set_hook(Box::new(move |panic_info| { - if let Ok(mut t) = crate::tui::Tui::new() { - if let Err(r) = t.exit() { - error!("Unable to exit Terminal: {:?}", r); - } - } - - #[cfg(not(debug_assertions))] - { - use human_panic::{handle_dump, print_msg, Metadata}; - let meta = Metadata { - version: env!("CARGO_PKG_VERSION").into(), - name: env!("CARGO_PKG_NAME").into(), - authors: env!("CARGO_PKG_AUTHORS").replace(':', ", ").into(), - homepage: env!("CARGO_PKG_HOMEPAGE").into(), - }; - - let file_path = handle_dump(&meta, panic_info); - // prints human-panic message - print_msg(file_path, &meta) - .expect("human-panic: printing error message to console failed"); - eprintln!("{}", panic_hook.panic_report(panic_info)); // prints color-eyre stack trace to stderr - } - let msg = format!("{}", panic_hook.panic_report(panic_info)); - log::error!("Error: {}", strip_ansi_escapes::strip_str(msg)); - - #[cfg(debug_assertions)] - { - // Better Panic stacktrace that is only enabled when debugging. - better_panic::Settings::auto() - .most_recent_first(false) - .lineno_suffix(true) - .verbosity(better_panic::Verbosity::Full) - .create_panic_handler()(panic_info); - } - - std::process::exit(libc::EXIT_FAILURE); - })); - Ok(()) -} - -pub fn get_data_dir() -> PathBuf { - let directory = if let Some(s) = DATA_FOLDER.clone() { - s - } else if let Some(proj_dirs) = project_directory() { - proj_dirs.data_local_dir().to_path_buf() - } else { - PathBuf::from(".").join(".data") - }; - directory -} - -pub fn get_config_dir() -> PathBuf { - let directory = if let Some(s) = CONFIG_FOLDER.clone() { - s - } else if let Some(proj_dirs) = project_directory() { - proj_dirs.config_local_dir().to_path_buf() - } else { - PathBuf::from(".").join(".config") - }; - directory -} - -pub fn initialize_logging() -> Result<()> { - let directory = get_data_dir(); - std::fs::create_dir_all(directory.clone())?; - let log_path = directory.join(LOG_FILE.clone()); - let log_file = std::fs::File::create(log_path)?; - std::env::set_var( - "RUST_LOG", - std::env::var("RUST_LOG") - .or_else(|_| std::env::var(LOG_ENV.clone())) - .unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME"))), - ); - let file_subscriber = tracing_subscriber::fmt::layer() - .with_file(true) - .with_line_number(true) - .with_writer(log_file) - .with_target(false) - .with_ansi(false) - .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); - tracing_subscriber::registry() - .with(file_subscriber) - .with(ErrorLayer::default()) - .init(); - Ok(()) -} - -/// Similar to the `std::dbg!` macro, but generates `tracing` events rather -/// than printing to stdout. -/// -/// By default, the verbosity level for the generated events is `DEBUG`, but -/// this can be customized. -#[macro_export] -macro_rules! trace_dbg { - (target: $target:expr, level: $level:expr, $ex:expr) => {{ - match $ex { - value => { - tracing::event!(target: $target, $level, ?value, stringify!($ex)); - value - } - } - }}; - (level: $level:expr, $ex:expr) => { - trace_dbg!(target: module_path!(), level: $level, $ex) - }; - (target: $target:expr, $ex:expr) => { - trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex) - }; - ($ex:expr) => { - trace_dbg!(level: tracing::Level::DEBUG, $ex) - }; -} - -pub fn version() -> String { - let author = clap::crate_authors!(); - - // let current_exe_path = PathBuf::from(clap::crate_name!()).display().to_string(); - let config_dir_path = get_config_dir().display().to_string(); - let data_dir_path = get_data_dir().display().to_string(); - - format!( - "\ -authors: {author} - -Config directory: {config_dir_path} -Data directory: {data_dir_path}" - ) -} diff --git a/src/built_in_functions/output.rs b/src/built_in_functions/output.rs index ced0c19..beffc06 100644 --- a/src/built_in_functions/output.rs +++ b/src/built_in_functions/output.rs @@ -1,26 +1,5 @@ -use lazy_static::lazy_static; - use crate::{BuiltInFunction, Error, Map, Result, Type, Value}; -lazy_static! { - static ref OUTPUT_MODE: OutputMode = { - if let Ok(variable) = std::env::var("DUST_OUTPUT_MODE") { - if variable == "SILENT" { - OutputMode::Silent - } else { - OutputMode::Normal - } - } else { - OutputMode::Normal - } - }; -} - -pub enum OutputMode { - Normal, - Silent, -} - pub struct Output; impl BuiltInFunction for Output { @@ -33,9 +12,7 @@ impl BuiltInFunction for Output { let value = arguments.first().unwrap(); - if let OutputMode::Normal = *OUTPUT_MODE { - println!("{value}"); - } + println!("{value}"); Ok(Value::default()) }