From a5644b42e1b368d4ffcaad78bb8dcfed3dcc6dc1 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Mon, 28 Jul 2025 21:49:57 -0700 Subject: [PATCH] add logging widget, needs some work --- Cargo.lock | 248 +++++++++++++++++++++++--------------------- Cargo.toml | 6 +- src/frontend/mod.rs | 111 ++++++++++++++------ src/frontend/ui.rs | 57 ---------- src/main.rs | 16 ++- 5 files changed, 225 insertions(+), 213 deletions(-) delete mode 100644 src/frontend/ui.rs diff --git a/Cargo.lock b/Cargo.lock index 59615f2..52e693c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + [[package]] name = "ascii" version = "1.1.0" @@ -264,6 +314,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "compact_str" version = "0.8.1" @@ -468,6 +524,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1051,6 +1120,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.13.0" @@ -1075,6 +1150,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "joecalsend" version = "0.1.0" @@ -1083,6 +1182,7 @@ dependencies = [ "chrono", "crossterm", "directories", + "env_logger", "figment", "futures", "julid-rs", @@ -1100,9 +1200,6 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tower-http", - "tracing", - "tracing-error", - "tracing-subscriber", "tui-logger", ] @@ -1203,15 +1300,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matchit" version = "0.8.4" @@ -1349,16 +1437,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1524,6 +1602,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl" version = "0.10.73" @@ -1574,12 +1658,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" version = "0.12.4" @@ -1656,6 +1734,21 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -1796,17 +1889,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1817,15 +1901,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -2088,15 +2166,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.3.0" @@ -2317,15 +2386,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - [[package]] name = "tinystr" version = "0.8.1" @@ -2494,21 +2554,9 @@ checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "tracing-core" version = "0.1.34" @@ -2516,46 +2564,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -2577,8 +2585,6 @@ dependencies = [ "log", "parking_lot", "ratatui", - "tracing", - "tracing-subscriber", "unicode-segmentation", ] @@ -2662,10 +2668,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "valuable" -version = "0.1.1" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" diff --git a/Cargo.toml b/Cargo.toml index 36f6623..329e998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ axum = { version = "0.8", features = ["macros"] } chrono = "0.4" crossterm = { version = "0.28", features = ["event-stream"] } directories = "6" +env_logger = "0.11.8" figment = { version = "0.10", features = ["toml", "test", "env"] } futures = "0.3" julid-rs = { version = "1", default-features = false, features = ["serde"] } @@ -25,7 +26,4 @@ sha256 = "1.6" thiserror = "2" tokio = { version = "1", default-features = false, features = ["time", "macros", "rt-multi-thread"] } tower-http = { version = "0.6", features = ["limit"] } -tracing = "0.1.41" -tracing-error = "0.2.1" -tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -tui-logger = { version = "0.17.3", features = ["crossterm", "tracing-support"] } +tui-logger = { version = "0.17", features = ["crossterm"] } diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index c8aa6c6..8749aa0 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -7,13 +7,13 @@ use joecalsend::{ error::{LocalSendError, Result}, models::Device, }; -use log::{error, info}; +use log::{LevelFilter, error, info}; use native_dialog::MessageDialogBuilder; use ratatui::{ - DefaultTerminal, + DefaultTerminal, Frame, buffer::Buffer, - layout::Rect, - style::Stylize, + layout::{Constraint, Direction, Layout, Margin, Rect}, + style::{Style, Stylize}, symbols::border, text::{Line, Text}, widgets::{Block, Paragraph, Widget}, @@ -22,9 +22,10 @@ use tokio::{ sync::mpsc::{UnboundedReceiver, unbounded_channel}, task::JoinSet, }; +use tui_logger::{TuiLoggerLevelOutput, TuiLoggerWidget, TuiWidgetState}; -pub mod ui; pub mod widgets; +use widgets::*; pub type Peers = BTreeMap; @@ -169,6 +170,10 @@ impl App { } } + fn draw(&self, frame: &mut Frame) { + frame.render_widget(self, frame.area()); + } + fn exit(&mut self) { self.screen.push(CurrentScreen::Stopping); } @@ -199,32 +204,80 @@ impl App { impl Widget for &App { fn render(self, area: Rect, buf: &mut Buffer) { - let title = Line::from(" Joecalsend ".bold()); - let instructions = Line::from(vec![ - " Send ".into(), - "".blue().bold(), - " Receive ".into(), - "".blue().bold(), - " Discover ".into(), - "".blue().bold(), - " Quit ".into(), - " ".blue().bold(), - ]); - let block = Block::bordered() - .title(title.centered()) - .title_bottom(instructions.centered()) - .border_set(border::THICK); + let [top, middle, bottom] = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(10), Constraint::Min(10), Constraint::Min(3)]) + .split(area) + .as_array() + .cloned() + .unwrap(); - let current_screen = format!( - "{:?}", - self.screen.last().copied().unwrap_or(CurrentScreen::Main) - ); - let text = Text::from(Line::from(current_screen.yellow())); + let [footer_left, footer_right] = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) + .split(bottom) + .as_array() + .cloned() + .unwrap(); - Paragraph::new(text) - .centered() - .block(block) - .render(area, buf); + let [header_left, header_right] = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(top) + .as_array() + .cloned() + .unwrap(); + + let logger = TuiLoggerWidget::default() + .output_separator('|') + .output_timestamp(Some("%F %H:%M:%S%.3f".to_string())) + .output_level(Some(TuiLoggerLevelOutput::Abbreviated)) + .output_target(false) + .output_file(false) + //.output_line(false) + .block(Block::bordered().border_set(border::THICK)) + .style(Style::default()) + .state(&TuiWidgetState::new().set_default_display_level(LevelFilter::Debug)); + + { + let title = Line::from(" Joecalsend ".bold()); + let instructions = Line::from(vec![ + " Send ".into(), + "".blue().bold(), + " Receive ".into(), + "".blue().bold(), + " Discover ".into(), + "".blue().bold(), + " Quit ".into(), + " ".blue().bold(), + ]); + let block = Block::bordered() + .title(title.centered()) + .title_bottom(instructions.centered()) + .border_set(border::THICK); + + let current_screen = format!( + "{:?}", + self.screen.last().copied().unwrap_or(CurrentScreen::Main) + ); + let text = Text::from(Line::from(current_screen.yellow())); + + Paragraph::new(text) + .centered() + .block(block) + .render(area, buf); + } + let mode = self.screen.last().unwrap(); + match mode { + CurrentScreen::Main => { + let peers = PeersWidget { peers: &self.peers }; + peers.render(footer_right.inner(Margin::new(1, 1)), buf); + NetworkInfoWidget.render(footer_left.inner(Margin::new(1, 1)), buf); + logger.render(header_right.inner(Margin::new(1, 2)), buf); + } + CurrentScreen::Receiving => {} + _ => {} + } } } diff --git a/src/frontend/ui.rs b/src/frontend/ui.rs deleted file mode 100644 index 8c527b0..0000000 --- a/src/frontend/ui.rs +++ /dev/null @@ -1,57 +0,0 @@ -use ratatui::{ - Frame, - layout::{Constraint, Direction, Layout, Margin, Rect}, -}; - -use crate::{App, frontend::widgets::*}; - -impl App { - pub fn draw(&self, frame: &mut Frame) { - let [top, middle, bottom] = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(3), Constraint::Min(3), Constraint::Min(3)]) - .split(frame.area()) - .as_array() - .cloned() - .unwrap(); - - let [footer_left, footer_right] = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) - .split(bottom) - .as_array() - .cloned() - .unwrap(); - - let peers = PeersWidget { peers: &self.peers }; - frame.render_widget(peers, footer_right.inner(Margin::new(1, 1))); - frame.render_widget(NetworkInfoWidget, footer_left.inner(Margin::new(1, 1))); - - // draw the main frame last - frame.render_widget(self, frame.area()); - } -} - -// helper function to create a centered rect using up certain percentage of the -// available rect `r` -fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { - // Cut the given rectangle into three vertical pieces - let popup_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Percentage((100 - percent_y) / 2), - Constraint::Percentage(percent_y), - Constraint::Percentage((100 - percent_y) / 2), - ]) - .split(r); - - // Then cut the middle vertical piece into three width-wise pieces - Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage((100 - percent_x) / 2), - Constraint::Percentage(percent_x), - Constraint::Percentage((100 - percent_x) / 2), - ]) - .split(popup_layout[1])[1] // Return the middle chunk -} diff --git a/src/main.rs b/src/main.rs index 09a7404..14c6c77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ #![feature(slice_as_array)] use frontend::App; -use joecalsend::{Config, error, models::Device}; +use joecalsend::{Config, error, error::LocalSendError, models::Device}; use local_ip_address::local_ip; use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig, V4IfAddr}; +use tui_logger::{ + LevelFilter, TuiLoggerFile, TuiLoggerLevelOutput, init_logger, set_default_level, set_log_file, +}; mod frontend; @@ -15,6 +18,16 @@ fn main() -> error::Result<()> { unreachable!() }; + init_logger(LevelFilter::Debug).map_err(|e| std::io::Error::other(format!("{e}")))?; + set_default_level(LevelFilter::Debug); + // let mut logfile = std::env::temp_dir(); + // logfile.push("joecalsend.log"); + // let file_options = TuiLoggerFile::new(logfile.to_str().unwrap()) + // .output_level(Some(TuiLoggerLevelOutput::Abbreviated)) + // .output_file(false) + // .output_separator(':'); + // set_log_file(file_options); + // for enumerating subnet peers when multicast fails (https://github.com/localsend/protocol?tab=readme-ov-file#32-http-legacy-mode) let mut _network_ip = ip; let nifs = NetworkInterface::show().unwrap(); @@ -36,7 +49,6 @@ fn main() -> error::Result<()> { let mut app = App::new(); let mut terminal = ratatui::init(); - let result = app.start_and_run(&mut terminal, config, device); ratatui::restore();