use std::path::PathBuf;
use std::str::FromStr;
use std::{env, fs};
use internet2::addr::ServiceAddr;
use log::LevelFilter;
#[derive(Copy, Clone, PartialEq, Eq, Debug, Display)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "lowercase")
)]
pub enum LogLevel {
#[display("error")]
Error = 0,
#[display("warn")]
Warn,
#[display("info")]
Info,
#[display("debug")]
Debug,
#[display("trace")]
Trace,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display, Error)]
#[display(doc_comments)]
pub struct LogLevelParseError(String);
impl FromStr for LogLevel {
type Err = LogLevelParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_ref() {
"error" | "errors" => LogLevel::Error,
"warn" | "warning" | "warnings" => LogLevel::Warn,
"info" => LogLevel::Info,
"debug" => LogLevel::Debug,
"trace" | "tracing" => LogLevel::Trace,
other => Err(LogLevelParseError(other.to_owned()))?,
})
}
}
impl From<u8> for LogLevel {
fn from(val: u8) -> Self { Self::from_verbosity_flag_count(val) }
}
impl From<LogLevel> for u8 {
fn from(log_level: LogLevel) -> Self { log_level.verbosity_flag_count() }
}
impl LogLevel {
pub fn verbosity_flag_count(&self) -> u8 {
match self {
LogLevel::Error => 0,
LogLevel::Warn => 1,
LogLevel::Info => 2,
LogLevel::Debug => 3,
LogLevel::Trace => 4,
}
}
pub fn from_verbosity_flag_count(level: u8) -> Self {
match level {
0 => LogLevel::Error,
1 => LogLevel::Warn,
2 => LogLevel::Info,
3 => LogLevel::Debug,
_ => LogLevel::Trace,
}
}
pub fn apply(&self) {
log::set_max_level(LevelFilter::Trace);
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", self.to_string());
}
env_logger::init();
}
}
pub trait Exec {
type Client: Sized;
type Error: std::error::Error;
fn exec(self, client: &mut Self::Client) -> Result<(), Self::Error>;
}
pub fn shell_setup<'endpoints>(
verbosity: u8,
endpoints: impl IntoIterator<Item = &'endpoints mut ServiceAddr>,
data_dir: &mut PathBuf,
pat: &[(&str, String)],
) {
LogLevel::from_verbosity_flag_count(verbosity).apply();
let mut data_dir_s = data_dir.display().to_string();
for (from, to) in pat {
data_dir_s = data_dir_s.replace(from, to);
}
*data_dir = PathBuf::from(shellexpand::tilde(&data_dir_s).to_string());
let data_dir_s = data_dir.display().to_string();
fs::create_dir_all(&data_dir)
.unwrap_or_else(|_| panic!("Unable to access data directory '{}'", data_dir.display()));
for addr in endpoints.into_iter() {
if let ServiceAddr::Ipc(ref mut path) = addr {
shell_expand_dir(path, &data_dir_s, pat);
}
}
}
pub fn shell_expand_dir(path: &mut String, data_dir: &str, pat: &[(&str, String)]) {
*path = path.replace("{data_dir}", data_dir);
*path = shellexpand::tilde(path).to_string();
for (from, to) in pat {
*path = path.replace(from, to);
}
}