use crate::{Result, util::read_var_from_env};
use std::ops::{Add, AddAssign, Sub, SubAssign};
pub (crate) const ENV_OPT_PREFIX: &str = "HCLOG_OPT_";
pub (crate) const ENV_OPT_FACADE: &str = "HCLOG_FACADE";
pub (crate) const ENV_OPT_LEVEL: &str = "HCLOG_LEVEL";
pub const NONE: Options = Options(0x0000);
pub const LINEBUFFERED: Options = Options(0x0001);
pub const TIMESTAMP: Options = Options(0x0002);
pub const DATESTAMP: Options = Options(0x0004);
pub const NANOSEC: Options = Options(0x0008);
pub const BINNAME: Options = Options(0x0010);
pub const PID: Options = Options(0x0020);
pub const TID: Options = Options(0x0040);
pub const MODULE: Options = Options(0x0080);
pub const SEVERITY: Options = Options(0x0100);
pub const SCOPE: Options = Options(0x0200);
pub const FUNC: Options = Options(0x0400);
pub const FILE: Options = Options(0x0800);
pub const LINE: Options = Options(0x1000);
pub const LOGCOMPAT: Options = Options(0x2000);
pub const EXACT_LVL_MATCH: Options = Options(0x4000);
#[allow(clippy::suspicious_arithmetic_impl)]
impl Add for Options {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl Sub for Options {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self(self.0 & !rhs.0)
}
}
impl AddAssign for Options {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl SubAssign for Options {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Options(u16);
impl Default for Options {
fn default() -> Self {
LINEBUFFERED + TIMESTAMP + DATESTAMP + NANOSEC + BINNAME +
PID + TID + MODULE + SEVERITY + FUNC + FILE + LINE + LOGCOMPAT
}
}
impl std::fmt::Debug for Options {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("[")?;
if self.has(LINEBUFFERED) { f.write_str("LINEBUFFERED, ")?; }
if self.has(TIMESTAMP) { f.write_str("TIMESTAMP, ")?; }
if self.has(DATESTAMP) { f.write_str("DATESTAMP, ")?; }
if self.has(NANOSEC) { f.write_str("NANOSEC, ")?; }
if self.has(BINNAME) { f.write_str("BINNAME, ")?; }
if self.has(PID) { f.write_str("PID, ")?; }
if self.has(TID) { f.write_str("TID, ")?; }
if self.has(MODULE) { f.write_str("MODULE, ")?; }
if self.has(SEVERITY) { f.write_str("SEVERITY, ")?; }
if self.has(SCOPE) { f.write_str("SCOPE, ")?; }
if self.has(FUNC) { f.write_str("FUNC, ")?; }
if self.has(FILE) { f.write_str("FILE, ")?; }
if self.has(LINE) { f.write_str("LINE, ")?; }
if self.has(LOGCOMPAT) { f.write_str("LOGCOMPAT, ")?; }
if self.has(EXACT_LVL_MATCH) { f.write_str("EXACT_LVL_MATCH, ")?; }
f.write_str("]")?;
Ok(())
}
}
impl Options {
pub fn new() -> Self {
NONE
}
#[inline(always)]
pub fn has(&self, flags: Options) -> bool {
self.0 & flags.0 == flags.0
}
#[inline(always)]
pub fn set(&mut self, flags: Options) {
*self += flags;
}
#[inline(always)]
pub fn unset(&mut self, flags: Options) {
*self -= flags;
}
pub fn reset(&mut self) -> Result<&Self> {
*self = Self::default();
self.parse_from_env()
}
#[doc(hidden)]
pub fn for_syslog(&mut self) {
*self -= TIMESTAMP + DATESTAMP + NANOSEC + BINNAME + PID + SEVERITY;
}
#[doc(hidden)]
fn opt_from_env(&mut self, key: &str, var: Options) -> Result<()> {
let envvar = format!("{}{}", ENV_OPT_PREFIX, key);
match read_var_from_env::<u16>(&envvar) {
Ok(v) => match v {
Some(0) => *self -= var,
Some(1) => *self += var,
Some(_) | None => (),
}
Err(e) => return Err(e),
}
Ok(())
}
#[doc(hidden)]
pub fn parse_from_env(&mut self) -> Result<&Self> {
self.opt_from_env("LINEBUFFERED", LINEBUFFERED)?;
self.opt_from_env("TIMESTAMP", TIMESTAMP)?;
self.opt_from_env("DATESTAMP", DATESTAMP)?;
self.opt_from_env("NANOSEC", NANOSEC)?;
self.opt_from_env("BINNAME", BINNAME)?;
self.opt_from_env("PID", PID)?;
self.opt_from_env("TID", TID)?;
self.opt_from_env("MODULE", MODULE)?;
self.opt_from_env("SEVERITY", SEVERITY)?;
self.opt_from_env("SCOPE", SCOPE)?;
self.opt_from_env("FUNC", FUNC)?;
self.opt_from_env("FILE", FILE)?;
self.opt_from_env("LINE", LINE)?;
self.opt_from_env("LOG_COMPAT", LOGCOMPAT)?;
self.opt_from_env("EXACT_LVL_MATCH", EXACT_LVL_MATCH)?;
Ok(self)
}
}
#[cfg(test)]
mod options_tests {
use super::*;
#[test]
fn read_from_env() {
std::env::set_var("FOO_BAR", "1");
assert!(read_var_from_env::<i32>("FOO_BAR").is_ok());
std::env::set_var("FOO_BAR", "BAZ");
assert!(read_var_from_env::<i32>("FOO_BAR").is_err());
std::env::set_var("FOO_BAR", "1");
assert_eq!(read_var_from_env::<i32>("FOO_BAR").unwrap().unwrap(), 1i32);
std::env::set_var("FOO_BAR", "BAZ");
assert_eq!(read_var_from_env::<String>("FOO_BAR").unwrap().unwrap(), "BAZ".to_owned())
}
#[test]
fn from_env_default() {
let mut default = Options::new();
assert!(default.has(NONE));
std::env::set_var("HCLOG_OPT_FUNC", "1");
default.parse_from_env().unwrap();
assert!(default.has(FUNC));
std::env::set_var("HCLOG_OPT_DATESTAMP", "1");
default.parse_from_env().unwrap();
assert!(default.has(DATESTAMP));
std::env::set_var("HCLOG_OPT_FUNC", "0");
default.parse_from_env().unwrap();
assert!(!default.has(FUNC));
default.reset().unwrap();
assert!(default.has(NONE));
}
#[test]
fn from_env_new() {
let new = Options::default();
assert!(!new.has(SCOPE));
assert!(new.has(TID));
assert!(new.has(LINEBUFFERED));
assert!(new.has(TIMESTAMP));
assert!(new.has(DATESTAMP));
}
#[test]
fn syslog_fields() {
let mut syslog = Options::default() + SCOPE;
syslog.for_syslog();
assert!(!syslog.has(TIMESTAMP));
assert!(!syslog.has(DATESTAMP));
assert!(!syslog.has(NANOSEC));
assert!(!syslog.has(BINNAME));
assert!(!syslog.has(PID));
assert!(!syslog.has(SEVERITY));
assert!(syslog.has(TID));
assert!(syslog.has(MODULE));
assert!(syslog.has(SCOPE));
assert!(syslog.has(FILE));
assert!(syslog.has(FUNC));
assert!(syslog.has(LINE));
}
}