@@ -385,20 +385,14 @@ pub type ShadowTheme = BTreeMap<String, TokenValues>;
385385
386386/// Convert a JSON number to a length value: `n * 4` + "px".
387387fn number_to_length ( n : & serde_json:: Number ) -> String {
388- if let Some ( i) = n. as_i64 ( ) {
389- let v = i * 4 ;
388+ // as_f64() covers both integer and float JSON numbers
389+ let val = n. as_f64 ( ) . unwrap_or ( 0.0 ) * 4.0 ;
390+ #[ allow( clippy:: cast_possible_truncation) ]
391+ if val. fract ( ) == 0.0 {
392+ let v = val as i64 ;
390393 format ! ( "{v}px" )
391- } else if let Some ( f) = n. as_f64 ( ) {
392- let val = f * 4.0 ;
393- #[ allow( clippy:: cast_possible_truncation) ]
394- if val. fract ( ) == 0.0 {
395- let v = val as i64 ;
396- format ! ( "{v}px" )
397- } else {
398- format ! ( "{val}px" )
399- }
400394 } else {
401- n . to_string ( )
395+ format ! ( "{val}px" )
402396 }
403397}
404398
@@ -726,32 +720,34 @@ impl Theme {
726720 for ( idx, val) in values. 0 . iter ( ) . enumerate ( ) {
727721 if let Some ( v) = val {
728722 let optimized = optimize_value ( v) ;
729- // For non-default variants, skip if same as default
730- if !is_default
731- && let Some ( default_theme) = themes. get ( default_key. as_str ( ) )
732- && let Some ( default_vals) = default_theme. get ( name)
733- && let Some ( Some ( default_v) ) = default_vals. 0 . get ( idx)
734- && optimize_value ( default_v) == optimized
735- {
736- continue ;
723+ let is_same_as_default = !is_default
724+ && themes
725+ . get ( default_key. as_str ( ) )
726+ . and_then ( |dt| dt. get ( name) )
727+ . and_then ( |dv| dv. 0 . get ( idx) )
728+ . is_some_and ( |dval| {
729+ dval. as_ref ( )
730+ . is_some_and ( |d| optimize_value ( d) == optimized)
731+ } ) ;
732+ if !is_same_as_default {
733+ level_map
734+ . entry ( idx)
735+ . or_default ( )
736+ . push ( format ! ( "--{name}:{optimized}" ) ) ;
737737 }
738- level_map
739- . entry ( idx)
740- . or_default ( )
741- . push ( format ! ( "--{name}:{optimized}" ) ) ;
742738 }
743739 }
744740 }
745741
746742 for ( level, vars) in & level_map {
747- if vars. is_empty ( ) {
748- continue ;
749- }
750- let vars_str = vars . join ( ";" ) ;
751- if * level == 0 {
752- write ! ( css, "{ selector}{{{vars_str}}}" ) . unwrap ( ) ;
753- } else if let Some ( bp ) = breakpoints . get ( * level ) {
754- write ! ( css , "@media(min-width:{bp}px){{{selector}{{{vars_str}}}}}" ) . unwrap ( ) ;
743+ if ! vars. is_empty ( ) {
744+ let vars_str = vars . join ( ";" ) ;
745+ if * level == 0 {
746+ write ! ( css , "{selector}{{{vars_str}}}" ) . unwrap ( ) ;
747+ } else if let Some ( bp ) = breakpoints . get ( * level ) {
748+ write ! ( css, "@media(min-width:{bp}px){{{ selector}{{{vars_str}}}}}" )
749+ . unwrap ( ) ;
750+ }
755751 }
756752 }
757753 }
@@ -2112,4 +2108,65 @@ mod tests {
21122108 let css = theme. to_css ( ) ;
21132109 assert_debug_snapshot ! ( css) ;
21142110 }
2111+
2112+ // ===== Coverage: TokenValues deserialization edge cases =====
2113+
2114+ #[ test]
2115+ fn test_token_values_deserialize_number ( ) {
2116+ // Covers TokenValues::deserialize Number branch (used by shadows)
2117+ let tv: TokenValues = serde_json:: from_str ( "42" ) . unwrap ( ) ;
2118+ assert_eq ! ( tv. 0 , vec![ Some ( "42" . to_string( ) ) ] ) ;
2119+ }
2120+
2121+ #[ test]
2122+ fn test_token_values_deserialize_array_with_number ( ) {
2123+ // Covers array Number branch in TokenValues::deserialize
2124+ let tv: TokenValues = serde_json:: from_str ( r#"["a", 10, null]"# ) . unwrap ( ) ;
2125+ assert_eq ! (
2126+ tv. 0 ,
2127+ vec![ Some ( "a" . to_string( ) ) , Some ( "10" . to_string( ) ) , None ]
2128+ ) ;
2129+ }
2130+
2131+ #[ test]
2132+ fn test_token_values_deserialize_invalid_array_item ( ) {
2133+ // Covers _ branch inside array match
2134+ let result: Result < TokenValues , _ > = serde_json:: from_str ( r#"[true]"# ) ;
2135+ assert ! ( result. is_err( ) ) ;
2136+ }
2137+
2138+ #[ test]
2139+ fn test_token_values_deserialize_invalid_type ( ) {
2140+ // Covers _ branch at top-level match
2141+ let result: Result < TokenValues , _ > = serde_json:: from_str ( "false" ) ;
2142+ assert ! ( result. is_err( ) ) ;
2143+ }
2144+
2145+ // ===== Coverage: number_to_length =====
2146+
2147+ #[ test]
2148+ fn test_number_to_length_float ( ) {
2149+ // Covers f64 non-integer branch
2150+ let n: serde_json:: Number = serde_json:: from_str ( "2.5" ) . unwrap ( ) ;
2151+ assert_eq ! ( number_to_length( & n) , "10px" ) ;
2152+ }
2153+
2154+ #[ test]
2155+ fn test_number_to_length_float_with_fraction ( ) {
2156+ // Covers f64 branch where result has a fractional part
2157+ let n: serde_json:: Number = serde_json:: from_str ( "1.3" ) . unwrap ( ) ;
2158+ let result = number_to_length ( & n) ;
2159+ assert ! ( result. ends_with( "px" ) ) ;
2160+ assert ! ( result. contains( "5.2" ) ) ; // 1.3 * 4 = 5.2
2161+ }
2162+
2163+ // ===== Coverage: write_themed_css_vars edge cases =====
2164+
2165+ #[ test]
2166+ fn test_write_themed_css_vars_empty ( ) {
2167+ // Covers early return for empty themes
2168+ let theme = Theme :: default ( ) ;
2169+ let css = theme. to_css ( ) ;
2170+ assert_eq ! ( css, "" ) ;
2171+ }
21152172}
0 commit comments