@@ -41,11 +41,20 @@ pub(crate) fn parse_bool(raw: &str) -> Result<bool, String> {
4141///
4242/// Returns a message naming the value when it is not a decimal integer.
4343pub ( crate ) fn parse_i64 ( raw : & str ) -> Result < i64 , String > {
44- raw. trim ( )
44+ trim_zero_decimal ( raw. trim ( ) )
4545 . parse :: < i64 > ( )
4646 . map_err ( |_| format ! ( "invalid integer `{raw}`" ) )
4747}
4848
49+ /// `cast`'s `trimZeroDecimal`, which drops an all-zero fraction before integer parsing, so `"8125.0"`
50+ /// is an integer setting while `"8125.5"` is not.
51+ fn trim_zero_decimal ( raw : & str ) -> & str {
52+ match raw. split_once ( '.' ) {
53+ Some ( ( integer, fraction) ) if !fraction. is_empty ( ) && fraction. bytes ( ) . all ( |byte| byte == b'0' ) => integer,
54+ _ => raw,
55+ }
56+ }
57+
4958/// `cast.ToFloat64E` for a string.
5059///
5160/// # Errors
@@ -63,8 +72,8 @@ pub(crate) fn parse_f64(raw: &str) -> Result<f64, String> {
6372///
6473/// # Errors
6574///
66- /// Returns an error for a value the Agent cannot cast to a boolean: an unrecognized string, a
67- /// floating-point number, or a compound value.
75+ /// Returns an error for a value the Agent cannot cast to a boolean: an unrecognized string or a
76+ /// compound value.
6877pub ( crate ) fn deserialize_bool < ' de , D > ( deserializer : D ) -> Result < bool , D :: Error >
6978where
7079 D : Deserializer < ' de > ,
@@ -128,7 +137,7 @@ impl Visitor<'_> for BoolVisitor {
128137 type Value = bool ;
129138
130139 fn expecting ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
131- f. write_str ( "a boolean, a boolean string, or an integer " )
140+ f. write_str ( "a boolean, a boolean string, or a number " )
132141 }
133142
134143 fn visit_bool < E : de:: Error > ( self , value : bool ) -> Result < bool , E > {
@@ -143,6 +152,10 @@ impl Visitor<'_> for BoolVisitor {
143152 Ok ( value != 0 )
144153 }
145154
155+ fn visit_f64 < E : de:: Error > ( self , value : f64 ) -> Result < bool , E > {
156+ Ok ( value != 0.0 )
157+ }
158+
146159 fn visit_str < E : de:: Error > ( self , value : & str ) -> Result < bool , E > {
147160 parse_bool ( value) . map_err ( |_| E :: invalid_value ( Unexpected :: Str ( value) , & self ) )
148161 }
@@ -359,23 +372,18 @@ mod tests {
359372 assert_eq ! ( as_bool( falsy. clone( ) ) , Ok ( false ) , "{falsy}" ) ;
360373 }
361374
362- // A non-zero integer is truthy, and a null reads as the zero value.
375+ // Any non-zero number is truthy, and a null reads as the zero value.
363376 assert_eq ! ( as_bool( json!( 2 ) ) , Ok ( true ) ) ;
364377 assert_eq ! ( as_bool( json!( -1 ) ) , Ok ( true ) ) ;
378+ assert_eq ! ( as_bool( json!( 1.0 ) ) , Ok ( true ) ) ;
379+ assert_eq ! ( as_bool( json!( 0.0 ) ) , Ok ( false ) ) ;
365380 assert_eq ! ( as_bool( json!( null) ) , Ok ( false ) ) ;
366381 }
367382
368383 #[ test]
369384 fn bool_rejects_what_go_rejects ( ) {
370- // `strconv.ParseBool` accepts none of these, and `cast` has no float branch for a boolean.
371- for rejected in [
372- json ! ( "yes" ) ,
373- json ! ( "on" ) ,
374- json ! ( "" ) ,
375- json ! ( 1.0 ) ,
376- json ! ( [ true ] ) ,
377- json ! ( { "a" : true } ) ,
378- ] {
385+ // `strconv.ParseBool` accepts none of these.
386+ for rejected in [ json ! ( "yes" ) , json ! ( "on" ) , json ! ( "" ) , json ! ( [ true ] ) , json ! ( { "a" : true } ) ] {
379387 assert ! ( as_bool( rejected. clone( ) ) . is_err( ) , "{rejected}" ) ;
380388 }
381389 }
@@ -388,14 +396,27 @@ mod tests {
388396 assert_eq ! ( as_int( json!( true ) ) , Ok ( 1 ) ) ;
389397 assert_eq ! ( as_int( json!( null) ) , Ok ( 0 ) ) ;
390398
399+ // `cast` drops an all-zero fraction from a numeric string.
400+ assert_eq ! ( as_int( json!( "8125.0" ) ) , Ok ( 8125 ) ) ;
401+ assert_eq ! ( as_int( json!( "8125.000" ) ) , Ok ( 8125 ) ) ;
402+ assert_eq ! ( as_int( json!( "-8125.0" ) ) , Ok ( -8125 ) ) ;
403+
391404 // Go truncates toward zero rather than rounding.
392405 assert_eq ! ( as_int( json!( 10.9 ) ) , Ok ( 10 ) ) ;
393406 assert_eq ! ( as_int( json!( -10.9 ) ) , Ok ( -10 ) ) ;
394407 }
395408
396409 #[ test]
397410 fn integer_rejects_unparseable_and_out_of_range_values ( ) {
398- for rejected in [ json ! ( "8125ms" ) , json ! ( "" ) , json ! ( "0x1f" ) , json ! ( 1e300 ) , json ! ( [ "8125" ] ) ] {
411+ for rejected in [
412+ json ! ( "8125ms" ) ,
413+ json ! ( "" ) ,
414+ json ! ( "0x1f" ) ,
415+ json ! ( "8125.5" ) ,
416+ json ! ( "8125." ) ,
417+ json ! ( 1e300 ) ,
418+ json ! ( [ "8125" ] ) ,
419+ ] {
399420 assert ! ( as_int( rejected. clone( ) ) . is_err( ) , "{rejected}" ) ;
400421 }
401422 }
0 commit comments