From 7cc4bb7eb147c4c7d2be6429013932151225703a Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 6 Sep 2021 16:09:09 +0900 Subject: [PATCH 1/3] fix table status --- src/components/table_status.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/table_status.rs b/src/components/table_status.rs index 7c0bee3..eebf268 100644 --- a/src/components/table_status.rs +++ b/src/components/table_status.rs @@ -45,20 +45,20 @@ impl TableStatusComponent { impl DrawableComponent for TableStatusComponent { fn draw(&mut self, f: &mut Frame, area: Rect, focused: bool) -> Result<()> { let status = Paragraph::new(Spans::from(vec![ - Span::from("rows: "), Span::from(format!( - "{}, ", + "rows: {}, ", self.row_count.map_or("-".to_string(), |c| c.to_string()) )), - Span::from("columns: "), Span::from(format!( - "{}, ", + "columns: {}, ", self.column_count.map_or("-".to_string(), |c| c.to_string()) )), - Span::from("engine: "), - Span::from(self.table.as_ref().map_or("-".to_string(), |c| { - c.engine.as_ref().map_or("-".to_string(), |e| e.to_string()) - })), + Span::from(format!( + "engine: {}", + self.table.as_ref().map_or("-".to_string(), |c| { + c.engine.as_ref().map_or("-".to_string(), |e| e.to_string()) + }) + )), ])) .block(Block::default().borders(Borders::TOP).style(if focused { Style::default() From 3c1e6d13e94efdd71922e9faccfb89ce424e0097 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 6 Sep 2021 16:09:30 +0900 Subject: [PATCH 2/3] early return error --- src/components/connections.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/connections.rs b/src/components/connections.rs index 6f223ba..455b2c2 100644 --- a/src/components/connections.rs +++ b/src/components/connections.rs @@ -86,13 +86,13 @@ impl DrawableComponent for ConnectionsComponent { let width = 80; let height = 20; let conns = &self.connections; - let connections: Vec = conns - .iter() - .map(|i| { - ListItem::new(vec![Spans::from(Span::raw(i.database_url().unwrap()))]) - .style(Style::default()) - }) - .collect(); + let mut connections: Vec = Vec::new(); + for c in conns { + connections.push( + ListItem::new(vec![Spans::from(Span::raw(c.database_url()?))]) + .style(Style::default()), + ) + } let tasks = List::new(connections) .block(Block::default().borders(Borders::ALL).title("Connections")) .highlight_style(Style::default().bg(Color::Blue)) From 78ba8459add723ce2ff1a2b6fd0f9a95e91eb6b6 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 6 Sep 2021 16:10:06 +0900 Subject: [PATCH 3/3] implement a log macro --- src/config.rs | 26 ++++++++++--------- src/log.rs | 69 +++++++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 11 +++++--- 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/src/config.rs b/src/config.rs index 3384e0b..3387549 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,4 @@ +use crate::log::LogLevel; use crate::Key; use serde::Deserialize; use std::fmt; @@ -17,6 +18,8 @@ pub struct Config { pub conn: Vec, #[serde(default)] pub key_config: KeyConfig, + #[serde(default)] + pub log_level: LogLevel, } #[derive(Debug, Deserialize, Clone)] @@ -51,6 +54,7 @@ impl Default for Config { database: None, }], key_config: KeyConfig::default(), + log_level: LogLevel::default(), } } } @@ -159,15 +163,15 @@ impl Connection { let user = self .user .as_ref() - .ok_or_else(|| anyhow::anyhow!("user is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type mysql needs the user field"))?; let host = self .host .as_ref() - .ok_or_else(|| anyhow::anyhow!("host is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type mysql needs the host field"))?; let port = self .port .as_ref() - .ok_or_else(|| anyhow::anyhow!("port is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type mysql needs the port field"))?; match self.database.as_ref() { Some(database) => Ok(format!( @@ -189,15 +193,15 @@ impl Connection { let user = self .user .as_ref() - .ok_or_else(|| anyhow::anyhow!("user is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type postgres needs the user field"))?; let host = self .host .as_ref() - .ok_or_else(|| anyhow::anyhow!("host is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type postgres needs the host field"))?; let port = self .port .as_ref() - .ok_or_else(|| anyhow::anyhow!("port is not set"))?; + .ok_or_else(|| anyhow::anyhow!("type postgres needs the port field"))?; match self.database.as_ref() { Some(database) => Ok(format!( @@ -216,12 +220,10 @@ impl Connection { } } DatabaseType::Sqlite => { - let path = self - .path - .as_ref() - .map_or(Err(anyhow::anyhow!("path is not set")), |path| { - Ok(path.to_str().unwrap()) - })?; + let path = self.path.as_ref().map_or( + Err(anyhow::anyhow!("type sqlite needs the path field")), + |path| Ok(path.to_str().unwrap()), + )?; Ok(format!("sqlite://{path}", path = path)) } diff --git a/src/log.rs b/src/log.rs index eecf27c..0a2da10 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,14 +1,63 @@ +use serde::Deserialize; + +#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Deserialize)] +pub enum LogLevel { + Quiet, + Error, + Info, +} + +impl Default for LogLevel { + fn default() -> Self { + Self::Info + } +} + +impl LogLevel { + pub fn is_writable(&self, level: &Self) -> bool { + use std::cmp::Ordering; + matches!(self.cmp(level), Ordering::Greater | Ordering::Equal) + } + + pub fn write(&self, level: &Self) -> Box { + if self.is_writable(level) { + match level { + Self::Error => Box::from(std::io::stderr()), + _ => Box::from(std::io::stdout()), + } + } else { + Box::from(std::io::sink()) + } + } +} + +impl From for &'static str { + fn from(log_level: LogLevel) -> &'static str { + match log_level { + LogLevel::Quiet => "quiet", + LogLevel::Info => "info", + LogLevel::Error => "error", + } + } +} + +impl std::str::FromStr for LogLevel { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "quiet" => Ok(Self::Quiet), + "info" | "all" => Ok(Self::Info), + "error" => Ok(Self::Error), + level => Err(format!("I don't know the log level of {:?}", level)), + } + } +} + #[macro_export] macro_rules! outln { - ($($expr:expr),+) => {{ - use std::io::{Write}; - use std::fs::OpenOptions; - let mut file = OpenOptions::new() - .write(true) - .create(true) - .append(true) - .open("gobang.log") - .unwrap(); - writeln!(file, $($expr),+).expect("Can't write output"); + ($config:ident#$level:path, $($expr:expr),+) => {{ + use $crate::log::LogLevel::*; + writeln!($config.log_level.write(&$level), $($expr),+).expect("Can't write output"); }} } diff --git a/src/main.rs b/src/main.rs index 241f73d..8a1e718 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,8 +22,6 @@ use tui::{backend::CrosstermBackend, Terminal}; #[tokio::main] async fn main() -> anyhow::Result<()> { - outln!("gobang logger"); - let value = crate::cli::parse(); let config = config::Config::new(&value.config)?; @@ -32,12 +30,17 @@ async fn main() -> anyhow::Result<()> { let backend = CrosstermBackend::new(io::stdout()); let mut terminal = Terminal::new(backend)?; let events = event::Events::new(250); - let mut app = App::new(config); + let mut app = App::new(config.clone()); terminal.clear()?; loop { - terminal.draw(|f| app.draw(f).unwrap())?; + terminal.draw(|f| { + if let Err(err) = app.draw(f) { + outln!(config#Error, "error: {}", err.to_string()); + std::process::exit(1); + } + })?; match events.next()? { Event::Input(key) => match app.event(key).await { Ok(state) => {