From 9da41db42708697eadcf40659045e99a44b8fa2e Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Tue, 20 Aug 2024 22:40:16 +0300 Subject: [PATCH] feat(main): implement readline functionality with rustylinez Including background log output --- Cargo.lock | 27 +++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 37 ++++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69c1a68..0ccae6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -302,6 +302,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitflags" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" + [[package]] name = "bitflags" version = "1.3.2" @@ -1198,6 +1204,7 @@ dependencies = [ "parse_datetime", "regex", "rustyline", + "rustylinez", "tokio", "xdg", ] @@ -1217,6 +1224,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" +dependencies = [ + "bitflags 0.4.0", + "libc", +] + [[package]] name = "nix" version = "0.26.4" @@ -1814,6 +1831,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustylinez" +version = "0.2.2" +source = "git+https://github.com/xeruf/rustylinez?rev=c2cd6178#c2cd617830fb15bd5c95c6f17809e91b95660889" +dependencies = [ + "libc", + "nix 0.5.1", + "unicode-width", +] + [[package]] name = "ryu" version = "1.0.17" diff --git a/Cargo.toml b/Cargo.toml index a8c052d..4ff604e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ interim = { version = "0.1", features = ["chrono"] } nostr-sdk = "0.34" # { git = "https://github.com/rust-nostr/nostr" } tokio = { version = "1.37", features = ["rt", "rt-multi-thread", "macros"] } regex = "1.10.5" +rustylinez = { git = "https://github.com/xeruf/rustylinez", rev = "c2cd6178" } [dev-dependencies] chrono-english = "0.1" diff --git a/src/main.rs b/src/main.rs index 4702d38..68c8fc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, VecDeque}; use std::env::{args, var}; use std::fs; use std::fs::File; -use std::io::{BufRead, BufReader, stdin, stdout, Write}; +use std::io::{BufRead, BufReader}; use std::iter::once; use std::ops::Sub; use std::path::PathBuf; @@ -11,12 +11,14 @@ use std::str::FromStr; use std::time::Duration; use colored::Colorize; -use env_logger::Builder; +use env_logger::{Builder, Target, WriteStyle}; use itertools::Itertools; use log::{debug, error, info, LevelFilter, trace, warn}; use nostr_sdk::prelude::*; use nostr_sdk::TagStandard::Hashtag; use regex::Regex; +use rustylinez::Editor; +use rustylinez::error::ReadlineError; use tokio::sync::mpsc; use tokio::sync::mpsc::Sender; use tokio::time::error::Elapsed; @@ -138,20 +140,22 @@ pub(crate) enum MostrMessage { #[tokio::main] async fn main() { + let mut rl = Editor::new(); + let mut args = args().skip(1).peekable(); if args.peek().is_some_and(|arg| arg == "--debug") { args.next(); - Builder::new() - .filter(None, LevelFilter::Debug) + let mut builder = Builder::new(); + builder.filter(None, LevelFilter::Debug) //.filter(Some("mostr"), LevelFilter::Trace) - .parse_default_env() - .init(); + .parse_default_env(); + builder } else { - colog::default_builder() - .filter(Some("nostr-relay-pool"), LevelFilter::Error) + let mut builder = colog::default_builder(); + builder.filter(Some("nostr-relay-pool"), LevelFilter::Error); //.filter(Some("nostr-relay-pool::relay::internal"), LevelFilter::Off) - .init(); - } + builder + }.write_style(WriteStyle::Always).target(Target::Pipe(Box::new(rl.get_printer()))).init(); let config_dir = or_warn!(BaseDirectories::new(), "Could not determine config directory") .and_then(|d| or_warn!(d.create_config_directory("mostr"), "Could not create config directory")) @@ -292,21 +296,19 @@ async fn main() { } } - let mut lines = stdin().lines(); loop { trace!("All Root Tasks:\n{}", relays.iter().map(|(url, tasks)| format!("{}: [{}]", url, tasks.children_of(None).map(|id| tasks.get_task_title(id)).join("; "))).join("\n")); println!(); let tasks = selected_relay.as_ref().and_then(|url| relays.get(url)).unwrap_or(&local_tasks); - print!( + let prompt = format!( "{} {}{}) ", selected_relay.as_ref().map_or("TEMP".to_string(), |url| url.to_string()).bright_black(), tasks.get_task_path(tasks.get_position()).bold(), tasks.get_prompt_suffix().italic(), ); - stdout().flush().unwrap(); - match lines.next() { - Some(Ok(input)) => { + match rl.readline(&prompt) { + Ok(input) => { let mut count = 0; while let Ok(notification) = notifications.try_recv() { if let RelayPoolNotification::Event { @@ -629,8 +631,9 @@ async fn main() { } or_warn!(tasks.print_tasks()); } - Some(Err(e)) => warn!("{}", e), - None => break, + Err(ReadlineError::Eof) => break, + Err(ReadlineError::Interrupted) => break, // TODO exit if prompt was empty, or clear + Err(e) => warn!("{}", e), } } println!();