native_theme/model/
icon_sizes.rs1use serde::{Deserialize, Serialize};
4
5#[serde_with::skip_serializing_none]
10#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
11#[serde(default)]
12pub struct IconSizes {
13 #[serde(rename = "toolbar_px")]
15 pub toolbar: Option<f32>,
16 #[serde(rename = "small_px")]
18 pub small: Option<f32>,
19 #[serde(rename = "large_px")]
21 pub large: Option<f32>,
22 #[serde(rename = "dialog_px")]
24 pub dialog: Option<f32>,
25 #[serde(rename = "panel_px")]
27 pub panel: Option<f32>,
28}
29
30impl IconSizes {
31 pub const FIELD_NAMES: &[&str] = &[
33 "toolbar_px",
34 "small_px",
35 "large_px",
36 "dialog_px",
37 "panel_px",
38 ];
39}
40
41impl_merge!(IconSizes {
42 option { toolbar, small, large, dialog, panel }
43});
44
45#[cfg(test)]
46#[allow(clippy::unwrap_used, clippy::expect_used)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn icon_sizes_default_is_empty() {
52 assert!(IconSizes::default().is_empty());
53 }
54
55 #[test]
56 fn icon_sizes_not_empty_when_toolbar_set() {
57 let s = IconSizes {
58 toolbar: Some(24.0),
59 ..Default::default()
60 };
61 assert!(!s.is_empty());
62 }
63
64 #[test]
65 fn icon_sizes_not_empty_when_any_field_set() {
66 for sizes in [
67 IconSizes {
68 toolbar: Some(24.0),
69 ..Default::default()
70 },
71 IconSizes {
72 small: Some(16.0),
73 ..Default::default()
74 },
75 IconSizes {
76 large: Some(32.0),
77 ..Default::default()
78 },
79 IconSizes {
80 dialog: Some(22.0),
81 ..Default::default()
82 },
83 IconSizes {
84 panel: Some(20.0),
85 ..Default::default()
86 },
87 ] {
88 assert!(!sizes.is_empty());
89 }
90 }
91
92 #[test]
93 fn icon_sizes_merge_overlay_wins() {
94 let mut base = IconSizes {
95 toolbar: Some(24.0),
96 small: Some(16.0),
97 large: None,
98 dialog: None,
99 panel: None,
100 };
101 let overlay = IconSizes {
102 toolbar: None,
103 small: Some(18.0),
104 large: Some(32.0),
105 dialog: None,
106 panel: None,
107 };
108 base.merge(&overlay);
109 assert_eq!(base.toolbar, Some(24.0)); assert_eq!(base.small, Some(18.0)); assert_eq!(base.large, Some(32.0)); assert_eq!(base.dialog, None);
113 assert_eq!(base.panel, None);
114 }
115
116 #[test]
117 fn icon_sizes_merge_none_preserves_base() {
118 let mut base = IconSizes {
119 toolbar: Some(24.0),
120 small: Some(16.0),
121 large: Some(32.0),
122 dialog: Some(22.0),
123 panel: Some(20.0),
124 };
125 let overlay = IconSizes::default();
126 base.merge(&overlay);
127 assert_eq!(base.toolbar, Some(24.0));
128 assert_eq!(base.small, Some(16.0));
129 assert_eq!(base.large, Some(32.0));
130 assert_eq!(base.dialog, Some(22.0));
131 assert_eq!(base.panel, Some(20.0));
132 }
133
134 #[test]
135 fn icon_sizes_toml_round_trip() {
136 let sizes = IconSizes {
137 toolbar: Some(24.0),
138 small: Some(16.0),
139 large: Some(32.0),
140 dialog: Some(22.0),
141 panel: Some(20.0),
142 };
143 let toml_str = toml::to_string(&sizes).unwrap();
144 let deserialized: IconSizes = toml::from_str(&toml_str).unwrap();
145 assert_eq!(deserialized, sizes);
146 }
147
148 #[test]
149 fn icon_sizes_toml_partial_round_trip() {
150 let sizes = IconSizes {
151 toolbar: Some(24.0),
152 ..Default::default()
153 };
154 let toml_str = toml::to_string(&sizes).unwrap();
155 let deserialized: IconSizes = toml::from_str(&toml_str).unwrap();
156 assert_eq!(deserialized, sizes);
157 assert!(deserialized.small.is_none());
158 }
159}