#![cfg(feature = "config")]
use log::Level;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::read_to_string;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use serde_yaml;
use crate::{
error::{Error, ErrorKind, Result, ToError},
LogDestination, DEFAULT_LOG_DEST, DEFAULT_LOG_LEVEL,
};
#[derive(Debug, Deserialize)]
struct LogConfigFile {
default_level: Option<String>,
mod_level: Option<HashMap<String, String>>,
log_dest: Option<String>,
log_stream: Option<PathBuf>,
color: Option<bool>,
brief_info: Option<bool>,
}
pub struct LogConfig {
default_level: Level,
mod_level: HashMap<String, Level>,
log_dest: LogDestination,
log_stream: Option<PathBuf>,
color: bool,
brief_info: bool,
}
impl<'a> LogConfig {
pub(crate) fn get_default_level(&'a self) -> Level {
self.default_level
}
pub(crate) fn get_mod_level(&'a self) -> &'a HashMap<String, Level> {
&self.mod_level
}
pub(crate) fn get_log_dest(&'a self) -> &'a LogDestination {
&self.log_dest
}
pub(crate) fn get_log_stream(&'a self) -> &'a Option<PathBuf> {
&self.log_stream
}
pub(crate) fn is_color(&self) -> bool {
self.color
}
pub(crate) fn is_brief_info(&self) -> bool {
self.brief_info
}
}
pub struct LogConfigBuilder {
inner: LogConfig,
}
impl<'a> LogConfigBuilder {
pub fn new() -> LogConfigBuilder {
LogConfigBuilder {
inner: LogConfig {
default_level: DEFAULT_LOG_LEVEL,
mod_level: HashMap::new(),
log_dest: DEFAULT_LOG_DEST,
log_stream: None,
color: false,
brief_info: false,
},
}
}
pub fn from_file<P: AsRef<Path>>(filename: P) -> Result<LogConfigBuilder> {
let config_path = filename.as_ref();
let config_str = &read_to_string(config_path).upstream_with_context(&format!(
"config::from_file: failed to read {}",
config_path.display()
))?;
let cfg_file: LogConfigFile = serde_yaml::from_str(config_str)
.upstream_with_context("failed to deserialze config from yaml")?;
let mut builder = LogConfigBuilder::new();
if let Some(ref level_str) = cfg_file.default_level {
builder.inner.default_level = Level::from_str(level_str)
.upstream_with_context(&format!("Invalid log level: '{}'", level_str))?;
}
if let Some(ref mod_level) = cfg_file.mod_level {
for (mod_name, mod_level) in mod_level {
builder.inner.mod_level.insert(
mod_name.clone(),
Level::from_str(mod_level).error_with_all(
ErrorKind::InvParam,
&format!("Invalid log level: '{}'", mod_level),
)?,
);
}
}
if let Some(ref dest_str) = cfg_file.log_dest {
let dest = LogDestination::from_str(dest_str)?;
if dest.is_stream_dest() {
if let Some(stream) = cfg_file.log_stream {
builder.inner.log_dest = dest;
builder.inner.log_stream = Some(stream)
} else {
return Err(Error::with_context(
ErrorKind::InvParam,
&format!(
"Missing log stream parameter for log destination {:?}",
dest
),
));
}
} else {
builder.inner.log_dest = dest;
}
}
if let Some(color) = cfg_file.color {
builder.inner.color = color;
}
if let Some(brief_info) = cfg_file.brief_info {
builder.inner.brief_info = brief_info;
}
Ok(builder)
}
pub fn set_default_level(&'a mut self, level: Level) -> &'a mut LogConfigBuilder {
self.inner.default_level = level;
self
}
pub fn set_mod_level(&'a mut self, module: &str, level: Level) -> &'a mut LogConfigBuilder {
let _dummy = self.inner.mod_level.insert(String::from(module), level);
self
}
pub fn set_log_dest(
&'a mut self,
dest: LogDestination,
file: Option<&PathBuf>,
) -> Result<&'a mut LogConfigBuilder> {
if dest.is_stream_dest() {
if let Some(stream) = file {
self.inner.log_stream = Some(stream.clone());
} else {
return Err(Error::with_context(
ErrorKind::InvParam,
&format!("Missing parameter stream for log destination: {:?}", dest),
));
}
}
self.inner.log_dest = dest;
Ok(self)
}
pub fn set_brief_info(&'a mut self, val: bool) {
self.inner.brief_info = val;
}
pub fn set_color(&'a mut self, val: bool) {
self.inner.color = val;
}
pub fn build(&'a self) -> &'a LogConfig {
&self.inner
}
}
impl Default for LogConfigBuilder {
fn default() -> Self {
Self::new()
}
}