11use std:: borrow:: Cow ;
2+ use std:: convert:: TryFrom ;
23use std:: num;
34use std:: str;
45use std:: str:: FromStr ;
5- use std:: string;
66
77use thiserror:: Error ;
8- use time;
98
109use crate :: facility;
1110use crate :: message:: { ProcId , StructuredData , SyslogMessage } ;
@@ -30,13 +29,19 @@ pub enum ParseErr {
3029 #[ error( "unicode error: {0}" ) ]
3130 BaseUnicodeError ( #[ from] str:: Utf8Error ) ,
3231 #[ error( "unicode error: {0}" ) ]
33- UnicodeError ( #[ from] string:: FromUtf8Error ) ,
32+ UnicodeError ( #[ from] std :: string:: FromUtf8Error ) ,
3433 #[ error( "unexpected input at character {0}" ) ]
3534 ExpectedTokenErr ( char ) ,
3635 #[ error( "integer conversion error: {0}" ) ]
3736 IntConversionErr ( #[ from] num:: ParseIntError ) ,
3837 #[ error( "missing field {0}" ) ]
3938 MissingField ( & ' static str ) ,
39+ #[ error( "invalid month number {0}" ) ]
40+ InvalidMonth ( u8 ) ,
41+ #[ error( "date had invalid field {0}" ) ]
42+ InvalidDate ( String ) ,
43+ #[ error( "date had invalid UTC offset" ) ]
44+ InvalidOffset ,
4045}
4146
4247// We parse with this super-duper-dinky hand-coded recursive descent parser because we don't really
@@ -182,8 +187,8 @@ fn parse_sde(sde: &str) -> ParseResult<((String, ParsedSDParams), &str)> {
182187
183188fn parse_sd ( structured_data_raw : & str ) -> ParseResult < ( StructuredData , & str ) > {
184189 let mut sd = StructuredData :: new_empty ( ) ;
185- if structured_data_raw. starts_with ( '-' ) {
186- return Ok ( ( sd, & structured_data_raw [ 1 .. ] ) ) ;
190+ if let Some ( rest ) = structured_data_raw. strip_prefix ( '-' ) {
191+ return Ok ( ( sd, rest ) ) ;
187192 }
188193 let mut rest = structured_data_raw;
189194 while !rest. is_empty ( ) {
@@ -204,8 +209,9 @@ fn parse_pri_val(pri: i32) -> ParseResult<(severity::SyslogSeverity, facility::S
204209 Ok ( ( sev, fac) )
205210}
206211
212+ /// Parse an i32
207213fn parse_num ( s : & str , min_digits : usize , max_digits : usize ) -> ParseResult < ( i32 , & str ) > {
208- let ( res, rest1) = take_while ( s, |c| c >= '0' && c <= '9' , max_digits) ;
214+ let ( res, rest1) = take_while ( s, |c| ( '0' ..= '9' ) . contains ( & c ) , max_digits) ;
209215 let rest = rest1. ok_or ( ParseErr :: UnexpectedEndOfInput ) ?;
210216 if res. len ( ) < min_digits {
211217 Err ( ParseErr :: TooFewDigits )
@@ -219,6 +225,22 @@ fn parse_num(s: &str, min_digits: usize, max_digits: usize) -> ParseResult<(i32,
219225 }
220226}
221227
228+ /// Parse an i32
229+ fn parse_num_generic < NT > ( s : & str , min_digits : usize , max_digits : usize ) -> ParseResult < ( NT , & str ) >
230+ where
231+ NT : FromStr < Err = num:: ParseIntError > ,
232+ {
233+ let ( res, rest1) = take_while ( s, |c| ( '0' ..='9' ) . contains ( & c) , max_digits) ;
234+ let rest = rest1. ok_or ( ParseErr :: UnexpectedEndOfInput ) ?;
235+ if res. len ( ) < min_digits {
236+ Err ( ParseErr :: TooFewDigits )
237+ } else if res. len ( ) > max_digits {
238+ Err ( ParseErr :: TooManyDigits )
239+ } else {
240+ Ok ( ( NT :: from_str ( res) . map_err ( ParseErr :: IntConversionErr ) ?, rest) )
241+ }
242+ }
243+
222244fn parse_decimal ( d : & str , min_digits : usize , max_digits : usize ) -> ParseResult < ( i32 , & str ) > {
223245 parse_num ( d, min_digits, max_digits) . map ( |( val, s) | {
224246 let mut multiplicand = 1 ;
@@ -231,52 +253,65 @@ fn parse_decimal(d: &str, min_digits: usize, max_digits: usize) -> ParseResult<(
231253 } )
232254}
233255
234- fn parse_timestamp ( m : & str ) -> ParseResult < ( Option < time:: Timespec > , & str ) > {
256+ fn parse_timestamp ( m : & str ) -> ParseResult < ( Option < time:: OffsetDateTime > , & str ) > {
235257 let mut rest = m;
236- if rest. starts_with ( '-' ) {
237- return Ok ( ( None , & rest[ 1 .. ] ) ) ;
258+ if let Some ( rest) = rest . strip_prefix ( '-' ) {
259+ return Ok ( ( None , rest) ) ;
238260 }
239- let mut tm = time:: empty_tm ( ) ;
240- tm. tm_year = take_item ! ( parse_num( rest, 4 , 4 ) , rest) - 1900 ;
261+ let year = take_item ! ( parse_num( rest, 4 , 4 ) , rest) ;
241262 take_char ! ( rest, '-' ) ;
242- tm. tm_mon = take_item ! ( parse_num( rest, 2 , 2 ) , rest) - 1 ;
263+ let month_num = take_item ! ( parse_num_generic( rest, 2 , 2 ) , rest) ;
264+ let month = time:: Month :: try_from ( month_num) . map_err ( |_| ParseErr :: InvalidMonth ( month_num) ) ?;
243265 take_char ! ( rest, '-' ) ;
244- tm. tm_mday = take_item ! ( parse_num( rest, 2 , 2 ) , rest) ;
266+ let mday = take_item ! ( parse_num_generic( rest, 2 , 2 ) , rest) ;
267+ let date = time:: Date :: from_calendar_date ( year, month, mday)
268+ . map_err ( |e| ParseErr :: InvalidDate ( e. name ( ) . to_string ( ) ) ) ?;
245269 take_char ! ( rest, 'T' ) ;
246- tm . tm_hour = take_item ! ( parse_num ( rest, 2 , 2 ) , rest) ;
270+ let hour = take_item ! ( parse_num_generic ( rest, 2 , 2 ) , rest) ;
247271 take_char ! ( rest, ':' ) ;
248- tm . tm_min = take_item ! ( parse_num ( rest, 2 , 2 ) , rest) ;
272+ let minute = take_item ! ( parse_num_generic ( rest, 2 , 2 ) , rest) ;
249273 take_char ! ( rest, ':' ) ;
250- tm . tm_sec = take_item ! ( parse_num ( rest, 2 , 2 ) , rest) ;
251- if rest. starts_with ( '.' ) {
274+ let second = take_item ! ( parse_num_generic ( rest, 2 , 2 ) , rest) ;
275+ let nano = if rest. starts_with ( '.' ) {
252276 take_char ! ( rest, '.' ) ;
253- tm. tm_nsec = take_item ! ( parse_decimal( rest, 1 , 6 ) , rest) ;
254- }
277+ take_item ! ( parse_decimal( rest, 1 , 6 ) , rest) as u32
278+ } else {
279+ 0
280+ } ;
281+ let time = time:: Time :: from_hms_nano ( hour, minute, second, nano)
282+ . map_err ( |e| ParseErr :: InvalidDate ( e. name ( ) . to_string ( ) ) ) ?;
255283 // Tm::utcoff is totally broken, don't use it.
256- let utc_offset_mins = match rest. chars ( ) . next ( ) {
257- None => 0 ,
284+ let utc_offset = match rest. chars ( ) . next ( ) {
285+ None => None ,
258286 Some ( 'Z' ) => {
259287 rest = & rest[ 1 ..] ;
260- 0
288+ None
261289 }
262290 Some ( c) => {
263291 let ( sign, irest) = match c {
264292 // Note: signs are backwards as per RFC3339
265- '-' => ( 1 , & rest[ 1 ..] ) ,
266- '+' => ( - 1 , & rest[ 1 ..] ) ,
293+ '-' => ( - 1 , & rest[ 1 ..] ) ,
294+ '+' => ( 1 , & rest[ 1 ..] ) ,
267295 _ => {
268296 return Err ( ParseErr :: InvalidUTCOffset ) ;
269297 }
270298 } ;
271- let hours = i32 :: from_str ( & irest[ 0 ..2 ] ) . map_err ( ParseErr :: IntConversionErr ) ?;
272- let minutes = i32 :: from_str ( & irest[ 3 ..5 ] ) . map_err ( ParseErr :: IntConversionErr ) ?;
299+ let hours = i8 :: from_str ( & irest[ 0 ..2 ] ) . map_err ( ParseErr :: IntConversionErr ) ?;
300+ let minutes = i8 :: from_str ( & irest[ 3 ..5 ] ) . map_err ( ParseErr :: IntConversionErr ) ?;
273301 rest = & irest[ 5 ..] ;
274- minutes * sign + hours * 60 * sign
302+ Some (
303+ time:: UtcOffset :: from_hms ( hours * sign, minutes * sign, 0 )
304+ . map_err ( |_| ParseErr :: InvalidOffset ) ?,
305+ )
275306 }
276307 } ;
277- tm = tm + time:: Duration :: minutes ( i64:: from ( utc_offset_mins) ) ;
278- tm. tm_isdst = -1 ;
279- Ok ( ( Some ( tm. to_utc ( ) . to_timespec ( ) ) , rest) )
308+ let naive_dt = time:: PrimitiveDateTime :: new ( date, time) ;
309+ let dt = if let Some ( utc_offset) = utc_offset {
310+ naive_dt. assume_offset ( utc_offset)
311+ } else {
312+ naive_dt. assume_utc ( )
313+ } ;
314+ Ok ( ( Some ( dt) , rest) )
280315}
281316
282317fn parse_term (
@@ -318,14 +353,10 @@ fn parse_message_s(m: &str) -> ParseResult<SyslogMessage> {
318353 take_char ! ( rest, ' ' ) ;
319354 let appname = take_item ! ( parse_term( rest, 1 , 48 ) , rest) ;
320355 take_char ! ( rest, ' ' ) ;
321- let procid_r = take_item ! ( parse_term( rest, 1 , 128 ) , rest) ;
322- let procid = match procid_r {
323- None => None ,
324- Some ( s) => Some ( match i32:: from_str ( & s) {
325- Ok ( n) => ProcId :: PID ( n) ,
326- Err ( _) => ProcId :: Name ( s) ,
327- } ) ,
328- } ;
356+ let procid = take_item ! ( parse_term( rest, 1 , 128 ) , rest) . map ( |s| match i32:: from_str ( & s) {
357+ Ok ( n) => ProcId :: PID ( n) ,
358+ Err ( _) => ProcId :: Name ( s) ,
359+ } ) ;
329360 take_char ! ( rest, ' ' ) ;
330361 let msgid = take_item ! ( parse_term( rest, 1 , 32 ) , rest) ;
331362 take_char ! ( rest, ' ' ) ;
@@ -340,8 +371,8 @@ fn parse_message_s(m: &str) -> ParseResult<SyslogMessage> {
340371 severity : sev,
341372 facility : fac,
342373 version,
343- timestamp : event_time. map ( |t| t. sec ) ,
344- timestamp_nanos : event_time. map ( |t| t. nsec ) ,
374+ timestamp : event_time. map ( |t| t. unix_timestamp ( ) ) ,
375+ timestamp_nanos : event_time. map ( |t| t. time ( ) . nanosecond ( ) ) ,
345376 hostname,
346377 appname,
347378 procid,
0 commit comments