use std::collections::HashSet;
use std::fmt;
use std::iter::IntoIterator;
use std::error;
use log::LevelFilter;
use append::Append;
use filter::Filter;
use {ConfigPrivateExt, PrivateConfigAppenderExt};
#[derive(Debug)]
pub struct Root {
level: LevelFilter,
appenders: Vec<String>,
}
impl Root {
pub fn builder() -> RootBuilder {
RootBuilder { appenders: vec![] }
}
pub fn level(&self) -> LevelFilter {
self.level
}
pub fn appenders(&self) -> &[String] {
&self.appenders
}
}
#[derive(Debug)]
pub struct RootBuilder {
appenders: Vec<String>,
}
impl RootBuilder {
pub fn appender<T>(mut self, appender: T) -> RootBuilder
where
T: Into<String>,
{
self.appenders.push(appender.into());
self
}
pub fn appenders<I>(mut self, appenders: I) -> RootBuilder
where
I: IntoIterator,
I::Item: Into<String>,
{
self.appenders.extend(appenders.into_iter().map(Into::into));
self
}
pub fn build(self, level: LevelFilter) -> Root {
Root {
level: level,
appenders: self.appenders,
}
}
}
#[derive(Debug)]
pub struct Appender {
name: String,
appender: Box<Append>,
filters: Vec<Box<Filter>>,
}
impl Appender {
pub fn builder() -> AppenderBuilder {
AppenderBuilder { filters: vec![] }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn appender(&self) -> &Append {
&*self.appender
}
pub fn filters(&self) -> &[Box<Filter>] {
&self.filters
}
}
impl PrivateConfigAppenderExt for Appender {
fn unpack(self) -> (String, Box<Append>, Vec<Box<Filter>>) {
let Appender {
name,
appender,
filters,
} = self;
(name, appender, filters)
}
}
#[derive(Debug)]
pub struct AppenderBuilder {
filters: Vec<Box<Filter>>,
}
impl AppenderBuilder {
pub fn filter(mut self, filter: Box<Filter>) -> AppenderBuilder {
self.filters.push(filter);
self
}
pub fn filters<I>(mut self, filters: I) -> AppenderBuilder
where
I: IntoIterator<Item = Box<Filter>>,
{
self.filters.extend(filters);
self
}
pub fn build<T>(self, name: T, appender: Box<Append>) -> Appender
where
T: Into<String>,
{
Appender {
name: name.into(),
appender: appender,
filters: self.filters,
}
}
}
#[derive(Debug)]
pub struct Logger {
name: String,
level: LevelFilter,
appenders: Vec<String>,
additive: bool,
}
impl Logger {
pub fn builder() -> LoggerBuilder {
LoggerBuilder {
appenders: vec![],
additive: true,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn level(&self) -> LevelFilter {
self.level
}
pub fn appenders(&self) -> &[String] {
&self.appenders
}
pub fn additive(&self) -> bool {
self.additive
}
}
#[derive(Debug)]
pub struct LoggerBuilder {
appenders: Vec<String>,
additive: bool,
}
impl LoggerBuilder {
pub fn appender<T>(mut self, appender: T) -> LoggerBuilder
where
T: Into<String>,
{
self.appenders.push(appender.into());
self
}
pub fn appenders<I>(mut self, appenders: I) -> LoggerBuilder
where
I: IntoIterator,
I::Item: Into<String>,
{
self.appenders.extend(appenders.into_iter().map(Into::into));
self
}
pub fn additive(mut self, additive: bool) -> LoggerBuilder {
self.additive = additive;
self
}
pub fn build<T>(self, name: T, level: LevelFilter) -> Logger
where
T: Into<String>,
{
Logger {
name: name.into(),
level: level,
appenders: self.appenders,
additive: self.additive,
}
}
}
#[derive(Debug)]
pub struct Config {
appenders: Vec<Appender>,
root: Root,
loggers: Vec<Logger>,
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder {
appenders: vec![],
loggers: vec![],
}
}
pub fn appenders(&self) -> &[Appender] {
&self.appenders
}
pub fn root(&self) -> &Root {
&self.root
}
pub fn loggers(&self) -> &[Logger] {
&self.loggers
}
}
pub struct ConfigBuilder {
appenders: Vec<Appender>,
loggers: Vec<Logger>,
}
impl ConfigBuilder {
pub fn appender(mut self, appender: Appender) -> ConfigBuilder {
self.appenders.push(appender);
self
}
pub fn appenders<I>(mut self, appenders: I) -> ConfigBuilder
where
I: IntoIterator<Item = Appender>,
{
self.appenders.extend(appenders);
self
}
pub fn logger(mut self, logger: Logger) -> ConfigBuilder {
self.loggers.push(logger);
self
}
pub fn loggers<I>(mut self, loggers: I) -> ConfigBuilder
where
I: IntoIterator<Item = Logger>,
{
self.loggers.extend(loggers);
self
}
pub fn build_lossy(self, mut root: Root) -> (Config, Vec<Error>) {
let mut errors = vec![];
let ConfigBuilder { appenders, loggers } = self;
let mut ok_appenders = vec![];
let mut appender_names = HashSet::new();
for appender in appenders {
if appender_names.insert(appender.name.clone()) {
ok_appenders.push(appender);
} else {
errors.push(Error::DuplicateAppenderName(appender.name));
}
}
let mut ok_root_appenders = vec![];
for appender in root.appenders {
if appender_names.contains(&appender) {
ok_root_appenders.push(appender);
} else {
errors.push(Error::NonexistentAppender(appender));
}
}
root.appenders = ok_root_appenders;
let mut ok_loggers = vec![];
let mut logger_names = HashSet::new();
for mut logger in loggers {
if !logger_names.insert(logger.name.clone()) {
errors.push(Error::DuplicateLoggerName(logger.name));
continue;
}
if let Err(err) = check_logger_name(&logger.name) {
errors.push(err);
continue;
}
let mut ok_logger_appenders = vec![];
for appender in logger.appenders {
if appender_names.contains(&appender) {
ok_logger_appenders.push(appender);
} else {
errors.push(Error::NonexistentAppender(appender));
}
}
logger.appenders = ok_logger_appenders;
ok_loggers.push(logger);
}
let config = Config {
appenders: ok_appenders,
root: root,
loggers: ok_loggers,
};
(config, errors)
}
pub fn build(self, root: Root) -> Result<Config, Errors> {
let (config, errors) = self.build_lossy(root);
if errors.is_empty() {
Ok(config)
} else {
Err(Errors { errors: errors })
}
}
}
fn check_logger_name(name: &str) -> Result<(), Error> {
if name.is_empty() {
return Err(Error::InvalidLoggerName(name.to_owned()));
}
let mut streak = 0;
for ch in name.chars() {
if ch == ':' {
streak += 1;
if streak > 2 {
return Err(Error::InvalidLoggerName(name.to_owned()));
}
} else {
if streak > 0 && streak != 2 {
return Err(Error::InvalidLoggerName(name.to_owned()));
}
streak = 0;
}
}
if streak > 0 {
Err(Error::InvalidLoggerName(name.to_owned()))
} else {
Ok(())
}
}
impl ConfigPrivateExt for Config {
fn unpack(self) -> (Vec<Appender>, Root, Vec<Logger>) {
let Config {
appenders,
root,
loggers,
} = self;
(appenders, root, loggers)
}
}
#[derive(Debug)]
pub struct Errors {
errors: Vec<Error>,
}
impl Errors {
pub fn errors(&self) -> &[Error] {
&self.errors
}
}
impl fmt::Display for Errors {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for error in &self.errors {
writeln!(fmt, "{}", error)?;
}
Ok(())
}
}
impl error::Error for Errors {
fn description(&self) -> &str {
"Errors encountered when validating a log4rs `Config`"
}
}
#[derive(Debug)]
pub enum Error {
DuplicateAppenderName(String),
NonexistentAppender(String),
DuplicateLoggerName(String),
InvalidLoggerName(String),
#[doc(hidden)] __Extensible,
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::DuplicateAppenderName(ref n) => write!(fmt, "Duplicate appender name `{}`", n),
Error::NonexistentAppender(ref n) => {
write!(fmt, "Reference to nonexistent appender: `{}`", n)
}
Error::DuplicateLoggerName(ref n) => write!(fmt, "Duplicate logger name `{}`", n),
Error::InvalidLoggerName(ref n) => write!(fmt, "Invalid logger name `{}`", n),
Error::__Extensible => unreachable!(),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
"An error constructing a log4rs `Config`"
}
}
#[cfg(test)]
mod test {
#[test]
fn check_logger_name() {
let tests = [
("", false),
("asdf", true),
("asdf::jkl", true),
("::", false),
("asdf::jkl::", false),
("asdf:jkl", false),
("asdf:::jkl", false),
("asdf::jkl::", false),
];
for &(ref name, expected) in &tests {
assert!(
expected == super::check_logger_name(name).is_ok(),
"{}",
name
);
}
}
}