]> git.lizzy.rs Git - rust.git/blob - src/liblog/directive.rs
Use if-let rather than map() in parse_logging_spec.
[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 std::ascii::AsciiExt;
12 use std::cmp;
13
14 #[derive(Debug, Clone)]
15 pub struct LogDirective {
16     pub name: Option<String>,
17     pub level: u32,
18 }
19
20 pub const LOG_LEVEL_NAMES: [&'static str; 4] = ["ERROR", "WARN", "INFO", "DEBUG"];
21
22 /// Parse an individual log level that is either a number or a symbolic log level
23 fn parse_log_level(level: &str) -> Option<u32> {
24     level.parse::<u32>()
25          .ok()
26          .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          })
30          .map(|p| cmp::min(p, ::MAX_LOG_LEVEL))
31 }
32
33 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo")
34 /// and return a vector with log directives.
35 ///
36 /// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
37 /// std::).  Also supports string log levels of error, warn, info, and debug
38 pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Option<String>) {
39     let mut dirs = Vec::new();
40
41     let mut parts = spec.split('/');
42     let mods = parts.next();
43     let filter = parts.next();
44     if parts.next().is_some() {
45         println!("warning: invalid logging spec '{}', ignoring it (too many '/'s)",
46                  spec);
47         return (dirs, None);
48     }
49     if let Some(m) = mods {
50         for s in m.split(',') {
51             if s.is_empty() {
52                 continue
53             }
54             let mut parts = s.split('=');
55             let (log_level, name) = match (parts.next(),
56                                            parts.next().map(|s| s.trim()),
57                                            parts.next()) {
58                 (Some(part0), None, None) => {
59                     // if the single argument is a log-level string or number,
60                     // treat that as a global fallback
61                     match parse_log_level(part0) {
62                         Some(num) => (num, None),
63                         None => (::MAX_LOG_LEVEL, Some(part0)),
64                     }
65                 }
66                 (Some(part0), Some(""), None) => (::MAX_LOG_LEVEL, Some(part0)),
67                 (Some(part0), Some(part1), None) => {
68                     match parse_log_level(part1) {
69                         Some(num) => (num, Some(part0)),
70                         _ => {
71                             println!("warning: invalid logging spec '{}', ignoring it", part1);
72                             continue
73                         }
74                     }
75                 }
76                 _ => {
77                     println!("warning: invalid logging spec '{}', ignoring it", s);
78                     continue
79                 }
80             };
81             dirs.push(LogDirective {
82                 name: name.map(str::to_owned),
83                 level: log_level,
84             });
85         }
86     }
87
88     (dirs, filter.map(str::to_owned))
89 }
90
91 #[cfg(test)]
92 mod tests {
93     use super::parse_logging_spec;
94
95     #[test]
96     fn parse_logging_spec_valid() {
97         let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4");
98         assert_eq!(dirs.len(), 3);
99         assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
100         assert_eq!(dirs[0].level, 1);
101
102         assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
103         assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
104
105         assert_eq!(dirs[2].name, Some("crate2".to_owned()));
106         assert_eq!(dirs[2].level, 4);
107         assert!(filter.is_none());
108     }
109
110     #[test]
111     fn parse_logging_spec_invalid_crate() {
112         // test parse_logging_spec with multiple = in specification
113         let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4");
114         assert_eq!(dirs.len(), 1);
115         assert_eq!(dirs[0].name, Some("crate2".to_owned()));
116         assert_eq!(dirs[0].level, 4);
117         assert!(filter.is_none());
118     }
119
120     #[test]
121     fn parse_logging_spec_invalid_log_level() {
122         // test parse_logging_spec with 'noNumber' as log level
123         let (dirs, filter) = parse_logging_spec("crate1::mod1=noNumber,crate2=4");
124         assert_eq!(dirs.len(), 1);
125         assert_eq!(dirs[0].name, Some("crate2".to_owned()));
126         assert_eq!(dirs[0].level, 4);
127         assert!(filter.is_none());
128     }
129
130     #[test]
131     fn parse_logging_spec_string_log_level() {
132         // test parse_logging_spec with 'warn' as log level
133         let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=warn");
134         assert_eq!(dirs.len(), 1);
135         assert_eq!(dirs[0].name, Some("crate2".to_owned()));
136         assert_eq!(dirs[0].level, ::WARN);
137         assert!(filter.is_none());
138     }
139
140     #[test]
141     fn parse_logging_spec_empty_log_level() {
142         // test parse_logging_spec with '' as log level
143         let (dirs, filter) = parse_logging_spec("crate1::mod1=wrong,crate2=");
144         assert_eq!(dirs.len(), 1);
145         assert_eq!(dirs[0].name, Some("crate2".to_owned()));
146         assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
147         assert!(filter.is_none());
148     }
149
150     #[test]
151     fn parse_logging_spec_global() {
152         // test parse_logging_spec with no crate
153         let (dirs, filter) = parse_logging_spec("warn,crate2=4");
154         assert_eq!(dirs.len(), 2);
155         assert_eq!(dirs[0].name, None);
156         assert_eq!(dirs[0].level, 2);
157         assert_eq!(dirs[1].name, Some("crate2".to_owned()));
158         assert_eq!(dirs[1].level, 4);
159         assert!(filter.is_none());
160     }
161
162     #[test]
163     fn parse_logging_spec_valid_filter() {
164         let (dirs, filter) = parse_logging_spec("crate1::mod1=1,crate1::mod2,crate2=4/abc");
165         assert_eq!(dirs.len(), 3);
166         assert_eq!(dirs[0].name, Some("crate1::mod1".to_owned()));
167         assert_eq!(dirs[0].level, 1);
168
169         assert_eq!(dirs[1].name, Some("crate1::mod2".to_owned()));
170         assert_eq!(dirs[1].level, ::MAX_LOG_LEVEL);
171
172         assert_eq!(dirs[2].name, Some("crate2".to_owned()));
173         assert_eq!(dirs[2].level, 4);
174         assert!(filter.is_some() && filter.unwrap().to_owned() == "abc");
175     }
176
177     #[test]
178     fn parse_logging_spec_invalid_crate_filter() {
179         let (dirs, filter) = parse_logging_spec("crate1::mod1=1=2,crate2=4/a.c");
180         assert_eq!(dirs.len(), 1);
181         assert_eq!(dirs[0].name, Some("crate2".to_owned()));
182         assert_eq!(dirs[0].level, 4);
183         assert!(filter.is_some() && filter.unwrap().to_owned() == "a.c");
184     }
185
186     #[test]
187     fn parse_logging_spec_empty_with_filter() {
188         let (dirs, filter) = parse_logging_spec("crate1/a*c");
189         assert_eq!(dirs.len(), 1);
190         assert_eq!(dirs[0].name, Some("crate1".to_owned()));
191         assert_eq!(dirs[0].level, ::MAX_LOG_LEVEL);
192         assert!(filter.is_some() && filter.unwrap().to_owned() == "a*c");
193     }
194 }