implement a log macro

pull/55/head
Takayuki Maeda 3 years ago
parent 3c1e6d13e9
commit 78ba8459ad

@ -1,3 +1,4 @@
use crate::log::LogLevel;
use crate::Key; use crate::Key;
use serde::Deserialize; use serde::Deserialize;
use std::fmt; use std::fmt;
@ -17,6 +18,8 @@ pub struct Config {
pub conn: Vec<Connection>, pub conn: Vec<Connection>,
#[serde(default)] #[serde(default)]
pub key_config: KeyConfig, pub key_config: KeyConfig,
#[serde(default)]
pub log_level: LogLevel,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
@ -51,6 +54,7 @@ impl Default for Config {
database: None, database: None,
}], }],
key_config: KeyConfig::default(), key_config: KeyConfig::default(),
log_level: LogLevel::default(),
} }
} }
} }
@ -159,15 +163,15 @@ impl Connection {
let user = self let user = self
.user .user
.as_ref() .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 let host = self
.host .host
.as_ref() .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 let port = self
.port .port
.as_ref() .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() { match self.database.as_ref() {
Some(database) => Ok(format!( Some(database) => Ok(format!(
@ -189,15 +193,15 @@ impl Connection {
let user = self let user = self
.user .user
.as_ref() .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 let host = self
.host .host
.as_ref() .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 let port = self
.port .port
.as_ref() .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() { match self.database.as_ref() {
Some(database) => Ok(format!( Some(database) => Ok(format!(
@ -216,12 +220,10 @@ impl Connection {
} }
} }
DatabaseType::Sqlite => { DatabaseType::Sqlite => {
let path = self let path = self.path.as_ref().map_or(
.path Err(anyhow::anyhow!("type sqlite needs the path field")),
.as_ref() |path| Ok(path.to_str().unwrap()),
.map_or(Err(anyhow::anyhow!("path is not set")), |path| { )?;
Ok(path.to_str().unwrap())
})?;
Ok(format!("sqlite://{path}", path = path)) Ok(format!("sqlite://{path}", path = path))
} }

@ -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<dyn std::io::Write> {
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<LogLevel> 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<LogLevel, Self::Err> {
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_export]
macro_rules! outln { macro_rules! outln {
($($expr:expr),+) => {{ ($config:ident#$level:path, $($expr:expr),+) => {{
use std::io::{Write}; use $crate::log::LogLevel::*;
use std::fs::OpenOptions; writeln!($config.log_level.write(&$level), $($expr),+).expect("Can't write output");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open("gobang.log")
.unwrap();
writeln!(file, $($expr),+).expect("Can't write output");
}} }}
} }

@ -22,8 +22,6 @@ use tui::{backend::CrosstermBackend, Terminal};
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
outln!("gobang logger");
let value = crate::cli::parse(); let value = crate::cli::parse();
let config = config::Config::new(&value.config)?; let config = config::Config::new(&value.config)?;
@ -32,12 +30,17 @@ async fn main() -> anyhow::Result<()> {
let backend = CrosstermBackend::new(io::stdout()); let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend)?;
let events = event::Events::new(250); let events = event::Events::new(250);
let mut app = App::new(config); let mut app = App::new(config.clone());
terminal.clear()?; terminal.clear()?;
loop { 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()? { match events.next()? {
Event::Input(key) => match app.event(key).await { Event::Input(key) => match app.event(key).await {
Ok(state) => { Ok(state) => {

Loading…
Cancel
Save