use std::env;
use std::mem;
use std::fmt;
use log::{Level, LevelFilter, Record, Metadata};
#[cfg(feature = "regex")]
#[path = "regex.rs"]
mod inner;
#[cfg(not(feature = "regex"))]
#[path = "string.rs"]
mod inner;
pub struct Filter {
    directives: Vec<Directive>,
    filter: Option<inner::Filter>,
}
pub struct Builder {
    directives: Vec<Directive>,
    filter: Option<inner::Filter>,
    built: bool,
}
#[derive(Debug)]
struct Directive {
    name: Option<String>,
    level: LevelFilter,
}
impl Filter {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    pub fn filter(&self) -> LevelFilter {
        self.directives.iter()
            .map(|d| d.level)
            .max()
            .unwrap_or(LevelFilter::Off)
    }
    
    pub fn matches(&self, record: &Record) -> bool {
        if !self.enabled(record.metadata()) {
            return false;
        }
        if let Some(filter) = self.filter.as_ref() {
            if !filter.is_match(&*record.args().to_string()) {
                return false;
            }
        }
        true
    }
    
    pub fn enabled(&self, metadata: &Metadata) -> bool {
        let level = metadata.level();
        let target = metadata.target();
        enabled(&self.directives, level, target)
    }
}
impl Builder {
    
    pub fn new() -> Builder {
        Builder {
            directives: Vec::new(),
            filter: None,
            built: false,
        }
    }
    
    pub fn from_env(env: &str) -> Builder {
        let mut builder = Builder::new();
        if let Ok(s) = env::var(env) {
            builder.parse(&s);
        }
        builder
    }
    
    pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
        self.filter(Some(module), level)
    }
    
    pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
        self.filter(None, level)
    }
    
    
    
    
    pub fn filter(&mut self,
                  module: Option<&str>,
                  level: LevelFilter) -> &mut Self {
        self.directives.push(Directive {
            name: module.map(|s| s.to_string()),
            level,
        });
        self
    }
    
    
    
    
    
    pub fn parse(&mut self, filters: &str) -> &mut Self {
        let (directives, filter) = parse_spec(filters);
        self.filter = filter;
        for directive in directives {
            self.directives.push(directive);
        }
        self
    }
    
    pub fn build(&mut self) -> Filter {
        assert!(!self.built, "attempt to re-use consumed builder");
        self.built = true;
        if self.directives.is_empty() {
            
            self.directives.push(Directive {
                name: None,
                level: LevelFilter::Error,
            });
        } else {
            
            
            self.directives.sort_by(|a, b| {
                let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
                let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
                alen.cmp(&blen)
            });
        }
        Filter {
            directives: mem::replace(&mut self.directives, Vec::new()),
            filter: mem::replace(&mut self.filter, None),
        }
    }
}
impl Default for Builder {
    fn default() -> Self {
        Builder::new()
    }
}
impl fmt::Debug for Filter {
    fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
        f.debug_struct("Filter")
            .field("filter", &self.filter)
            .field("directives", &self.directives)
            .finish()
    }
}
impl fmt::Debug for Builder {
    fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
        if self.built {
            f.debug_struct("Filter")
            .field("built", &true)
            .finish()
        } else {
            f.debug_struct("Filter")
            .field("filter", &self.filter)
            .field("directives", &self.directives)
            .finish()
        }
    }
}
fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
    let mut dirs = Vec::new();
    let mut parts = spec.split('/');
    let mods = parts.next();
    let filter = parts.next();
    if parts.next().is_some() {
        eprintln!("warning: invalid logging spec '{}', \
                  ignoring it (too many '/'s)", spec);
        return (dirs, None);
    }
    mods.map(|m| { for s in m.split(',') {
        if s.len() == 0 { continue }
        let mut parts = s.split('=');
        let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
            (Some(part0), None, None) => {
                
                
                match part0.parse() {
                    Ok(num) => (num, None),
                    Err(_) => (LevelFilter::max(), Some(part0)),
                }
            }
            (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
            (Some(part0), Some(part1), None) => {
                match part1.parse() {
                    Ok(num) => (num, Some(part0)),
                    _ => {
                        eprintln!("warning: invalid logging spec '{}', \
                                  ignoring it", part1);
                        continue
                    }
                }
            },
            _ => {
                eprintln!("warning: invalid logging spec '{}', \
                          ignoring it", s);
                continue
            }
        };
        dirs.push(Directive {
            name: name.map(|s| s.to_string()),
            level: log_level,
        });
    }});
    let filter = filter.map_or(None, |filter| {
        match inner::Filter::new(filter) {
            Ok(re) => Some(re),
            Err(e) => {
                eprintln!("warning: invalid regex filter - {}", e);
                None
            }
        }
    });
    return (dirs, filter);
}
fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
    
    for directive in directives.iter().rev() {
        match directive.name {
            Some(ref name) if !target.starts_with(&**name) => {},
            Some(..) | None => {
                return level <= directive.level
            }
        }
    }
    false
}
#[cfg(test)]
mod tests {
    use log::{Level, LevelFilter};
    use super::{Builder, Filter, Directive, parse_spec, enabled};
    fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
        let mut logger = Builder::new().build();
        logger.directives = dirs;
        logger
    }
    #[test]
    fn filter_info() {
        let logger = Builder::new().filter(None, LevelFilter::Info).build();
        assert!(enabled(&logger.directives, Level::Info, "crate1"));
        assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
    }
    #[test]
    fn filter_beginning_longest_match() {
        let logger = Builder::new()
                        .filter(Some("crate2"), LevelFilter::Info)
                        .filter(Some("crate2::mod"), LevelFilter::Debug)
                        .filter(Some("crate1::mod1"), LevelFilter::Warn)
                        .build();
        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
    }
    #[test]
    fn parse_default() {
        let logger = Builder::new().parse("info,crate1::mod1=warn").build();
        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
    }
    #[test]
    fn match_full_path() {
        let logger = make_logger_filter(vec![
            Directive {
                name: Some("crate2".to_string()),
                level: LevelFilter::Info
            },
            Directive {
                name: Some("crate1::mod1".to_string()),
                level: LevelFilter::Warn
            }
        ]);
        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
        assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
        assert!(enabled(&logger.directives, Level::Info, "crate2"));
        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
    }
    #[test]
    fn no_match() {
        let logger = make_logger_filter(vec![
            Directive { name: Some("crate2".to_string()), level: LevelFilter::Info },
            Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn }
        ]);
        assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
    }
    #[test]
    fn match_beginning() {
        let logger = make_logger_filter(vec![
            Directive { name: Some("crate2".to_string()), level: LevelFilter::Info },
            Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn }
        ]);
        assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
    }
    #[test]
    fn match_beginning_longest_match() {
        let logger = make_logger_filter(vec![
            Directive { name: Some("crate2".to_string()), level: LevelFilter::Info },
            Directive { name: Some("crate2::mod".to_string()), level: LevelFilter::Debug },
            Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn }
        ]);
        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
    }
    #[test]
    fn match_default() {
        let logger = make_logger_filter(vec![
            Directive { name: None, level: LevelFilter::Info },
            Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Warn }
        ]);
        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
    }
    #[test]
    fn zero_level() {
        let logger = make_logger_filter(vec![
            Directive { name: None, level: LevelFilter::Info },
            Directive { name: Some("crate1::mod1".to_string()), level: LevelFilter::Off }
        ]);
        assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
    }
    #[test]
    fn parse_spec_valid() {
        let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Error);
        assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
        assert_eq!(dirs[1].level, LevelFilter::max());
        assert_eq!(dirs[2].name, Some("crate2".to_string()));
        assert_eq!(dirs[2].level, LevelFilter::Debug);
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_invalid_crate() {
        
        let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Debug);
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_invalid_level() {
        
        let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Debug);
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_string_level() {
        
        let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Warn);
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_empty_level() {
        
        let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::max());
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_global() {
        
        let (dirs, filter) = parse_spec("warn,crate2=debug");
        assert_eq!(dirs.len(), 2);
        assert_eq!(dirs[0].name, None);
        assert_eq!(dirs[0].level, LevelFilter::Warn);
        assert_eq!(dirs[1].name, Some("crate2".to_string()));
        assert_eq!(dirs[1].level, LevelFilter::Debug);
        assert!(filter.is_none());
    }
    #[test]
    fn parse_spec_valid_filter() {
        let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
        assert_eq!(dirs.len(), 3);
        assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Error);
        assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
        assert_eq!(dirs[1].level, LevelFilter::max());
        assert_eq!(dirs[2].name, Some("crate2".to_string()));
        assert_eq!(dirs[2].level, LevelFilter::Debug);
        assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
    }
    #[test]
    fn parse_spec_invalid_crate_filter() {
        let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate2".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::Debug);
        assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
    }
    #[test]
    fn parse_spec_empty_with_filter() {
        let (dirs, filter) = parse_spec("crate1/a*c");
        assert_eq!(dirs.len(), 1);
        assert_eq!(dirs[0].name, Some("crate1".to_string()));
        assert_eq!(dirs[0].level, LevelFilter::max());
        assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
    }
}