88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11+ use regex:: Regex ;
1112use std:: ascii:: AsciiExt ;
1213use std:: cmp;
1314
@@ -28,14 +29,23 @@ fn parse_log_level(level: &str) -> Option<u32> {
2829 } ) . map ( |p| cmp:: min ( p, :: MAX_LOG_LEVEL ) )
2930}
3031
31- /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
32+ /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1/foo ")
3233/// and return a vector with log directives.
3334///
3435/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in
3536/// std::). Also supports string log levels of error, warn, info, and debug
36- pub fn parse_logging_spec ( spec : & str ) -> Vec < LogDirective > {
37+ pub fn parse_logging_spec ( spec : & str ) -> ( Vec < LogDirective > , Option < Regex > ) {
3738 let mut dirs = Vec :: new ( ) ;
38- for s in spec. split ( ',' ) {
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 ( ',' ) {
3949 if s. len ( ) == 0 { continue }
4050 let mut parts = s. split ( '=' ) ;
4151 let ( log_level, name) = match ( parts. next ( ) , parts. next ( ) . map ( |s| s. trim ( ) ) , parts. next ( ) ) {
@@ -68,8 +78,19 @@ pub fn parse_logging_spec(spec: &str) -> Vec<LogDirective> {
6878 name : name. map ( |s| s. to_string ( ) ) ,
6979 level : log_level,
7080 } ) ;
71- }
72- return dirs;
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) ;
7394}
7495
7596#[ cfg( test) ]
@@ -78,7 +99,7 @@ mod tests {
7899
79100 #[ test]
80101 fn parse_logging_spec_valid ( ) {
81- let dirs = parse_logging_spec ( "crate1::mod1=1,crate1::mod2,crate2=4" ) ;
102+ let ( dirs, filter ) = parse_logging_spec ( "crate1::mod1=1,crate1::mod2,crate2=4" ) ;
82103 let dirs = dirs. as_slice ( ) ;
83104 assert_eq ! ( dirs. len( ) , 3 ) ;
84105 assert_eq ! ( dirs[ 0 ] . name, Some ( "crate1::mod1" . to_string( ) ) ) ;
@@ -89,57 +110,99 @@ mod tests {
89110
90111 assert_eq ! ( dirs[ 2 ] . name, Some ( "crate2" . to_string( ) ) ) ;
91112 assert_eq ! ( dirs[ 2 ] . level, 4 ) ;
113+ assert ! ( filter. is_none( ) ) ;
92114 }
93115
94116 #[ test]
95117 fn parse_logging_spec_invalid_crate ( ) {
96118 // test parse_logging_spec with multiple = in specification
97- let dirs = parse_logging_spec ( "crate1::mod1=1=2,crate2=4" ) ;
119+ let ( dirs, filter ) = parse_logging_spec ( "crate1::mod1=1=2,crate2=4" ) ;
98120 let dirs = dirs. as_slice ( ) ;
99121 assert_eq ! ( dirs. len( ) , 1 ) ;
100122 assert_eq ! ( dirs[ 0 ] . name, Some ( "crate2" . to_string( ) ) ) ;
101123 assert_eq ! ( dirs[ 0 ] . level, 4 ) ;
124+ assert ! ( filter. is_none( ) ) ;
102125 }
103126
104127 #[ test]
105128 fn parse_logging_spec_invalid_log_level ( ) {
106129 // test parse_logging_spec with 'noNumber' as log level
107- let dirs = parse_logging_spec ( "crate1::mod1=noNumber,crate2=4" ) ;
130+ let ( dirs, filter ) = parse_logging_spec ( "crate1::mod1=noNumber,crate2=4" ) ;
108131 let dirs = dirs. as_slice ( ) ;
109132 assert_eq ! ( dirs. len( ) , 1 ) ;
110133 assert_eq ! ( dirs[ 0 ] . name, Some ( "crate2" . to_string( ) ) ) ;
111134 assert_eq ! ( dirs[ 0 ] . level, 4 ) ;
135+ assert ! ( filter. is_none( ) ) ;
112136 }
113137
114138 #[ test]
115139 fn parse_logging_spec_string_log_level ( ) {
116140 // test parse_logging_spec with 'warn' as log level
117- let dirs = parse_logging_spec ( "crate1::mod1=wrong,crate2=warn" ) ;
141+ let ( dirs, filter ) = parse_logging_spec ( "crate1::mod1=wrong,crate2=warn" ) ;
118142 let dirs = dirs. as_slice ( ) ;
119143 assert_eq ! ( dirs. len( ) , 1 ) ;
120144 assert_eq ! ( dirs[ 0 ] . name, Some ( "crate2" . to_string( ) ) ) ;
121145 assert_eq ! ( dirs[ 0 ] . level, :: WARN ) ;
146+ assert ! ( filter. is_none( ) ) ;
122147 }
123148
124149 #[ test]
125150 fn parse_logging_spec_empty_log_level ( ) {
126151 // test parse_logging_spec with '' as log level
127- let dirs = parse_logging_spec ( "crate1::mod1=wrong,crate2=" ) ;
152+ let ( dirs, filter ) = parse_logging_spec ( "crate1::mod1=wrong,crate2=" ) ;
128153 let dirs = dirs. as_slice ( ) ;
129154 assert_eq ! ( dirs. len( ) , 1 ) ;
130155 assert_eq ! ( dirs[ 0 ] . name, Some ( "crate2" . to_string( ) ) ) ;
131156 assert_eq ! ( dirs[ 0 ] . level, :: MAX_LOG_LEVEL ) ;
157+ assert ! ( filter. is_none( ) ) ;
132158 }
133159
134160 #[ test]
135161 fn parse_logging_spec_global ( ) {
136162 // test parse_logging_spec with no crate
137- let dirs = parse_logging_spec ( "warn,crate2=4" ) ;
163+ let ( dirs, filter ) = parse_logging_spec ( "warn,crate2=4" ) ;
138164 let dirs = dirs. as_slice ( ) ;
139165 assert_eq ! ( dirs. len( ) , 2 ) ;
140166 assert_eq ! ( dirs[ 0 ] . name, None ) ;
141167 assert_eq ! ( dirs[ 0 ] . level, 2 ) ;
142168 assert_eq ! ( dirs[ 1 ] . name, Some ( "crate2" . to_string( ) ) ) ;
143169 assert_eq ! ( dirs[ 1 ] . level, 4 ) ;
170+ assert ! ( filter. is_none( ) ) ;
171+ }
172+
173+ #[ test]
174+ fn parse_logging_spec_valid_filter ( ) {
175+ let ( dirs, filter) = parse_logging_spec ( "crate1::mod1=1,crate1::mod2,crate2=4/abc" ) ;
176+ let dirs = dirs. as_slice ( ) ;
177+ assert_eq ! ( dirs. len( ) , 3 ) ;
178+ assert_eq ! ( dirs[ 0 ] . name, Some ( "crate1::mod1" . to_string( ) ) ) ;
179+ assert_eq ! ( dirs[ 0 ] . level, 1 ) ;
180+
181+ assert_eq ! ( dirs[ 1 ] . name, Some ( "crate1::mod2" . to_string( ) ) ) ;
182+ assert_eq ! ( dirs[ 1 ] . level, :: MAX_LOG_LEVEL ) ;
183+
184+ assert_eq ! ( dirs[ 2 ] . name, Some ( "crate2" . to_string( ) ) ) ;
185+ assert_eq ! ( dirs[ 2 ] . level, 4 ) ;
186+ assert ! ( filter. is_some( ) && filter. unwrap( ) . to_string( ) . as_slice( ) == "abc" ) ;
187+ }
188+
189+ #[ test]
190+ fn parse_logging_spec_invalid_crate_filter ( ) {
191+ let ( dirs, filter) = parse_logging_spec ( "crate1::mod1=1=2,crate2=4/a.c" ) ;
192+ let dirs = dirs. as_slice ( ) ;
193+ assert_eq ! ( dirs. len( ) , 1 ) ;
194+ assert_eq ! ( dirs[ 0 ] . name, Some ( "crate2" . to_string( ) ) ) ;
195+ assert_eq ! ( dirs[ 0 ] . level, 4 ) ;
196+ assert ! ( filter. is_some( ) && filter. unwrap( ) . to_string( ) . as_slice( ) == "a.c" ) ;
197+ }
198+
199+ #[ test]
200+ fn parse_logging_spec_empty_with_filter ( ) {
201+ let ( dirs, filter) = parse_logging_spec ( "crate1/a*c" ) ;
202+ let dirs = dirs. as_slice ( ) ;
203+ assert_eq ! ( dirs. len( ) , 1 ) ;
204+ assert_eq ! ( dirs[ 0 ] . name, Some ( "crate1" . to_string( ) ) ) ;
205+ assert_eq ! ( dirs[ 0 ] . level, :: MAX_LOG_LEVEL ) ;
206+ assert ! ( filter. is_some( ) && filter. unwrap( ) . to_string( ) . as_slice( ) == "a*c" ) ;
144207 }
145208}
0 commit comments