@@ -150,7 +150,7 @@ impl ExtendedParser for i64 {
150150 }
151151 }
152152
153- match parse ( input, true ) {
153+ match parse ( input, ParseTarget :: Integral , & [ ] ) {
154154 Ok ( v) => into_i64 ( v) ,
155155 Err ( e) => Err ( e. map ( into_i64) ) ,
156156 }
@@ -187,7 +187,7 @@ impl ExtendedParser for u64 {
187187 }
188188 }
189189
190- match parse ( input, true ) {
190+ match parse ( input, ParseTarget :: Integral , & [ ] ) {
191191 Ok ( v) => into_u64 ( v) ,
192192 Err ( e) => Err ( e. map ( into_u64) ) ,
193193 }
@@ -219,7 +219,7 @@ impl ExtendedParser for f64 {
219219 Ok ( v)
220220 }
221221
222- match parse ( input, false ) {
222+ match parse ( input, ParseTarget :: Decimal , & [ ] ) {
223223 Ok ( v) => into_f64 ( v) ,
224224 Err ( e) => Err ( e. map ( into_f64) ) ,
225225 }
@@ -231,14 +231,15 @@ impl ExtendedParser for ExtendedBigDecimal {
231231 fn extended_parse (
232232 input : & str ,
233233 ) -> Result < ExtendedBigDecimal , ExtendedParserError < ' _ , ExtendedBigDecimal > > {
234- parse ( input, false )
234+ parse ( input, ParseTarget :: Decimal , & [ ] )
235235 }
236236}
237237
238- fn parse_special_value (
239- input : & str ,
238+ fn parse_special_value < ' a > (
239+ input : & ' a str ,
240240 negative : bool ,
241- ) -> Result < ExtendedBigDecimal , ExtendedParserError < ' _ , ExtendedBigDecimal > > {
241+ allowed_suffixes : & ' a [ ( char , u32 ) ] ,
242+ ) -> Result < ExtendedBigDecimal , ExtendedParserError < ' a , ExtendedBigDecimal > > {
242243 let input_lc = input. to_ascii_lowercase ( ) ;
243244
244245 // Array of ("String to match", return value when sign positive, when sign negative)
@@ -254,7 +255,14 @@ fn parse_special_value(
254255 if negative {
255256 special = -special;
256257 }
257- let match_len = str. len ( ) ;
258+ let mut match_len = str. len ( ) ;
259+ if let Some ( ch) = input. chars ( ) . nth ( str. chars ( ) . count ( ) ) {
260+ if allowed_suffixes. iter ( ) . any ( |( c, _) | ch == * c) {
261+ // multiplying is unnecessary for these special values, but we have to note that
262+ // we processed the character to avoid a partial match error
263+ match_len += 1 ;
264+ }
265+ }
258266 return if input. len ( ) == match_len {
259267 Ok ( special)
260268 } else {
@@ -381,24 +389,34 @@ fn construct_extended_big_decimal<'a>(
381389 Ok ( ExtendedBigDecimal :: BigDecimal ( bd) )
382390}
383391
392+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
393+ pub ( crate ) enum ParseTarget {
394+ Decimal ,
395+ Integral ,
396+ Duration ,
397+ }
398+
384399// TODO: As highlighted by clippy, this function _is_ high cognitive complexity, jumps
385400// around between integer and float parsing, and should be split in multiple parts.
386401#[ allow( clippy:: cognitive_complexity) ]
387- fn parse (
388- input : & str ,
389- integral_only : bool ,
390- ) -> Result < ExtendedBigDecimal , ExtendedParserError < ' _ , ExtendedBigDecimal > > {
402+ pub ( crate ) fn parse < ' a > (
403+ input : & ' a str ,
404+ target : ParseTarget ,
405+ allowed_suffixes : & ' a [ ( char , u32 ) ] ,
406+ ) -> Result < ExtendedBigDecimal , ExtendedParserError < ' a , ExtendedBigDecimal > > {
391407 // Parse the " and ' prefixes separately
392- if let Some ( rest) = input. strip_prefix ( [ '\'' , '"' ] ) {
393- let mut chars = rest. char_indices ( ) . fuse ( ) ;
394- let v = chars
395- . next ( )
396- . map ( |( _, c) | ExtendedBigDecimal :: BigDecimal ( u32:: from ( c) . into ( ) ) ) ;
397- return match ( v, chars. next ( ) ) {
398- ( Some ( v) , None ) => Ok ( v) ,
399- ( Some ( v) , Some ( ( i, _) ) ) => Err ( ExtendedParserError :: PartialMatch ( v, & rest[ i..] ) ) ,
400- ( None , _) => Err ( ExtendedParserError :: NotNumeric ) ,
401- } ;
408+ if target != ParseTarget :: Duration {
409+ if let Some ( rest) = input. strip_prefix ( [ '\'' , '"' ] ) {
410+ let mut chars = rest. char_indices ( ) . fuse ( ) ;
411+ let v = chars
412+ . next ( )
413+ . map ( |( _, c) | ExtendedBigDecimal :: BigDecimal ( u32:: from ( c) . into ( ) ) ) ;
414+ return match ( v, chars. next ( ) ) {
415+ ( Some ( v) , None ) => Ok ( v) ,
416+ ( Some ( v) , Some ( ( i, _) ) ) => Err ( ExtendedParserError :: PartialMatch ( v, & rest[ i..] ) ) ,
417+ ( None , _) => Err ( ExtendedParserError :: NotNumeric ) ,
418+ } ;
419+ }
402420 }
403421
404422 let trimmed_input = input. trim_ascii_start ( ) ;
@@ -419,7 +437,7 @@ fn parse(
419437 let ( base, rest) = if let Some ( rest) = unsigned. strip_prefix ( '0' ) {
420438 if let Some ( rest) = rest. strip_prefix ( [ 'x' , 'X' ] ) {
421439 ( Base :: Hexadecimal , rest)
422- } else if integral_only {
440+ } else if target == ParseTarget :: Integral {
423441 // Binary/Octal only allowed for integer parsing.
424442 if let Some ( rest) = rest. strip_prefix ( [ 'b' , 'B' ] ) {
425443 ( Base :: Binary , rest)
@@ -447,7 +465,7 @@ fn parse(
447465 }
448466
449467 // Parse fractional/exponent part of the number for supported bases.
450- if matches ! ( base, Base :: Decimal | Base :: Hexadecimal ) && !integral_only {
468+ if matches ! ( base, Base :: Decimal | Base :: Hexadecimal ) && target != ParseTarget :: Integral {
451469 // Parse the fractional part of the number if there can be one and the input contains
452470 // a '.' decimal separator.
453471 if matches ! ( chars. peek( ) , Some ( & ( _, '.' ) ) ) {
@@ -493,13 +511,24 @@ fn parse(
493511
494512 // If nothing has been parsed, check if this is a special value, or declare the parsing unsuccessful
495513 if let Some ( ( 0 , _) ) = chars. peek ( ) {
496- return if integral_only {
514+ return if target == ParseTarget :: Integral {
497515 Err ( ExtendedParserError :: NotNumeric )
498516 } else {
499- parse_special_value ( unsigned, negative)
517+ parse_special_value ( unsigned, negative, allowed_suffixes )
500518 } ;
501519 }
502520
521+ if let Some ( ( _, ch) ) = chars. peek ( ) {
522+ if let Some ( times) = allowed_suffixes
523+ . iter ( )
524+ . find ( |( c, _) | ch == c)
525+ . map ( |& ( _, t) | t)
526+ {
527+ chars. next ( ) ;
528+ digits *= times;
529+ }
530+ }
531+
503532 let ebd_result = construct_extended_big_decimal ( digits, negative, base, scale, exponent) ;
504533
505534 // Return what has been parsed so far. If there are extra characters, mark the
0 commit comments