]> git.lizzy.rs Git - rust.git/blob - src/liblog/directive.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / liblog / directive.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use regex::Regex;
12 use std::ascii::AsciiExt;
13 use std::cmp;
14
15 #[derive(Show, Clone)]
16 pub struct LogDirective {
17     pub name: Option<String>,
18     pub level: u32,
19 }
20
21 pub static LOG_LEVEL_NAMES: [&'static str; 4] = ["ERROR", "WARN", "INFO",
22                                                "DEBUG"];
23
24 /// Parse an individual log level that is either a number or a symbolic log level
25 fn parse_log_level(level: &str) -> Option<u32> {
26     level.parse::<u32>().or_else(|| {
27         let pos = LOG_LEVEL_NAMES.iter().position(|&name| name.eq_ignore_ascii_case(level));
28         pos.map(|p| p as u32 + 1)
29     }).map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
30 }
31
32 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
33 /// and return a vector with log directives.
34 ///
35 /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
36 /// std::).  Also supports string log levels of error, warn, info, and debug
37 pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<Regex>) {
38     let mut dirs = Vec::new();
39
40     let mut parts = spec.split('/');
41     let mods = parts.next();
42     let filter = parts.next();
43     if parts.next().is_some() {
44         println!("warning: invalid logging spec '{}', \
45                  ignoring it (too many '/'s)", spec);
46         return (dirs, None);
47     }
48     mods.map(|m| { for s in m.split(',') {
49         if s.len() == 0 { continue }
50         let mut parts = s.split('=');
51         let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
52             (Some(part0), None, None) => {
53                 // if the single argument is a log-level string or number,
54                 // treat that as a global fallback
55                 match parse_log_level(part0) {
56                     Some(num) => (num, None),
57                     None => (::MAX_LOG_LEVEL, Some(part0)),
58                 }
59             }
60             (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
61             (Some(part0), Some(part1), None) => {
62                 match parse_log_level(part1) {
63                     Some(num) => (num, Some(part0)),
64                     _ => {
65                         println!("warning: invalid logging spec '{}', \
66                                  ignoring it", part1);
67                         continue
68                     }
69                 }
70             },
71             _ => {
72                 println!("warning: invalid logging spec '{}', \
73                          ignoring it", s);
74                 continue
75             }
76         };
77         dirs.push(LogDirective {
78             name: name.map(|s| s.to_string()),
79             level: log_level,
80         });
81     }});
82
83     let filter = filter.map_or(None, |filter| {
84         match Regex::new(filter) {
85             Ok(re) => Some(re),
86             Err(e) => {
87                 println!("warning: invalid regex filter - {}", e);
88                 None
89             }
90         }
91     });
92
93     return (dirs, filter);
94 }
95
96 #[cfg(test)]
97 mod tests {
98     use super::parse_logging_spec;
99
100     #[test]
101     fn parse_logging_spec_valid() {
102         let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
103         assert_eq!(dirs.len(), 3);
104         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
105         assert_eq!(dirs[0].level, 1);
106
107         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
108         assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
109
110         assert_eq!(dirs[2].name, Some("crate2".to_string()));
111         assert_eq!(dirs[2].level, 4);
112         assert!(filter.is_none());
113     }
114
115     #[test]
116     fn parse_logging_spec_invalid_crate() {
117         // test parse_logging_spec with multiple = in specification
118         let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
119         assert_eq!(dirs.len(), 1);
120         assert_eq!(dirs[0].name, Some("crate2".to_string()));
121         assert_eq!(dirs[0].level, 4);
122         assert!(filter.is_none());
123     }
124
125     #[test]
126     fn parse_logging_spec_invalid_log_level() {
127         // test parse_logging_spec with 'noNumber' as log level
128         let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
129         assert_eq!(dirs.len(), 1);
130         assert_eq!(dirs[0].name, Some("crate2".to_string()));
131         assert_eq!(dirs[0].level, 4);
132         assert!(filter.is_none());
133     }
134
135     #[test]
136     fn parse_logging_spec_string_log_level() {
137         // test parse_logging_spec with 'warn' as log level
138         let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
139         assert_eq!(dirs.len(), 1);
140         assert_eq!(dirs[0].name, Some("crate2".to_string()));
141         assert_eq!(dirs[0].level, ::WARN);
142         assert!(filter.is_none());
143     }
144
145     #[test]
146     fn parse_logging_spec_empty_log_level() {
147         // test parse_logging_spec with '' as log level
148         let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
149         assert_eq!(dirs.len(), 1);
150         assert_eq!(dirs[0].name, Some("crate2".to_string()));
151         assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
152         assert!(filter.is_none());
153     }
154
155     #[test]
156     fn parse_logging_spec_global() {
157         // test parse_logging_spec with no crate
158         let (dirs, filter) = parse_logging_spec("warn,crate2=4");
159         assert_eq!(dirs.len(), 2);
160         assert_eq!(dirs[0].name, None);
161         assert_eq!(dirs[0].level, 2);
162         assert_eq!(dirs[1].name, Some("crate2".to_string()));
163         assert_eq!(dirs[1].level, 4);
164         assert!(filter.is_none());
165     }
166
167     #[test]
168     fn parse_logging_spec_valid_filter() {
169         let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
170         assert_eq!(dirs.len(), 3);
171         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
172         assert_eq!(dirs[0].level, 1);
173
174         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
175         assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
176
177         assert_eq!(dirs[2].name, Some("crate2".to_string()));
178         assert_eq!(dirs[2].level, 4);
179         assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
180     }
181
182     #[test]
183     fn parse_logging_spec_invalid_crate_filter() {
184         let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
185         assert_eq!(dirs.len(), 1);
186         assert_eq!(dirs[0].name, Some("crate2".to_string()));
187         assert_eq!(dirs[0].level, 4);
188         assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
189     }
190
191     #[test]
192     fn parse_logging_spec_empty_with_filter() {
193         let (dirs, filter) = parse_logging_spec("crate1/a*c");
194         assert_eq!(dirs.len(), 1);
195         assert_eq!(dirs[0].name, Some("crate1".to_string()));
196         assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
197         assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
198     }
199 }