dendryform_core/
layout.rs1use serde::de::{self, MapAccess, Visitor};
4use serde::{Deserialize, Deserializer, Serialize};
5
6#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
10#[serde(rename_all = "snake_case")]
11#[non_exhaustive]
12pub enum TierLayout {
13 Single,
15 Grid {
17 columns: u32,
19 },
20 #[default]
22 Auto,
23}
24
25impl<'de> Deserialize<'de> for TierLayout {
26 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
27 where
28 D: Deserializer<'de>,
29 {
30 deserializer.deserialize_any(TierLayoutVisitor)
31 }
32}
33
34struct TierLayoutVisitor;
35
36impl<'de> Visitor<'de> for TierLayoutVisitor {
37 type Value = TierLayout;
38
39 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.write_str("\"single\", \"auto\", or a map like {grid: {columns: N}}")
41 }
42
43 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
44 match value {
45 "single" => Ok(TierLayout::Single),
46 "auto" => Ok(TierLayout::Auto),
47 other => Err(de::Error::unknown_variant(
48 other,
49 &["single", "auto", "grid"],
50 )),
51 }
52 }
53
54 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
55 where
56 A: MapAccess<'de>,
57 {
58 let key: String = map
59 .next_key()?
60 .ok_or_else(|| de::Error::custom("expected a layout variant key"))?;
61
62 match key.as_str() {
63 "grid" => {
64 #[derive(Deserialize)]
65 struct GridData {
66 columns: u32,
67 }
68 let data: GridData = map.next_value()?;
69 Ok(TierLayout::Grid {
70 columns: data.columns,
71 })
72 }
73 "single" => {
74 let _: serde::de::IgnoredAny = map.next_value()?;
76 Ok(TierLayout::Single)
77 }
78 "auto" => {
79 let _: serde::de::IgnoredAny = map.next_value()?;
80 Ok(TierLayout::Auto)
81 }
82 other => Err(de::Error::unknown_variant(
83 other,
84 &["single", "auto", "grid"],
85 )),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_default_is_auto() {
96 assert_eq!(TierLayout::default(), TierLayout::Auto);
97 }
98
99 #[test]
100 fn test_serde_single() {
101 let layout = TierLayout::Single;
102 let json = serde_json::to_string(&layout).unwrap();
103 assert_eq!(json, "\"single\"");
104 let deserialized: TierLayout = serde_json::from_str(&json).unwrap();
105 assert_eq!(layout, deserialized);
106 }
107
108 #[test]
109 fn test_serde_grid() {
110 let layout = TierLayout::Grid { columns: 3 };
111 let json = serde_json::to_string(&layout).unwrap();
112 let deserialized: TierLayout = serde_json::from_str(&json).unwrap();
113 assert_eq!(layout, deserialized);
114 }
115
116 #[test]
117 fn test_yaml_single_string() {
118 let yaml = "single";
119 let layout: TierLayout = serde_yml::from_str(yaml).unwrap();
120 assert_eq!(layout, TierLayout::Single);
121 }
122
123 #[test]
124 fn test_yaml_auto_string() {
125 let yaml = "auto";
126 let layout: TierLayout = serde_yml::from_str(yaml).unwrap();
127 assert_eq!(layout, TierLayout::Auto);
128 }
129
130 #[test]
131 fn test_yaml_grid_map() {
132 let yaml = "grid:\n columns: 4";
133 let layout: TierLayout = serde_yml::from_str(yaml).unwrap();
134 assert_eq!(layout, TierLayout::Grid { columns: 4 });
135 }
136
137 #[test]
138 fn test_yaml_single_map_form() {
139 let yaml = "single: ~";
140 let layout: TierLayout = serde_yml::from_str(yaml).unwrap();
141 assert_eq!(layout, TierLayout::Single);
142 }
143
144 #[test]
145 fn test_yaml_auto_map_form() {
146 let yaml = "auto: ~";
147 let layout: TierLayout = serde_yml::from_str(yaml).unwrap();
148 assert_eq!(layout, TierLayout::Auto);
149 }
150
151 #[test]
152 fn test_yaml_unknown_string_variant() {
153 let yaml = "\"unknown_variant\"";
154 let result: Result<TierLayout, _> = serde_yml::from_str(yaml);
155 assert!(result.is_err());
156 }
157
158 #[test]
159 fn test_yaml_unknown_map_variant() {
160 let yaml = "unknown_key:\n foo: bar";
161 let result: Result<TierLayout, _> = serde_yml::from_str(yaml);
162 assert!(result.is_err());
163 }
164
165 #[test]
166 fn test_json_single_string() {
167 let json = "\"single\"";
168 let layout: TierLayout = serde_json::from_str(json).unwrap();
169 assert_eq!(layout, TierLayout::Single);
170 }
171
172 #[test]
173 fn test_json_auto_string() {
174 let json = "\"auto\"";
175 let layout: TierLayout = serde_json::from_str(json).unwrap();
176 assert_eq!(layout, TierLayout::Auto);
177 }
178
179 #[test]
180 fn test_json_grid_map() {
181 let json = r#"{"grid":{"columns":3}}"#;
182 let layout: TierLayout = serde_json::from_str(json).unwrap();
183 assert_eq!(layout, TierLayout::Grid { columns: 3 });
184 }
185
186 #[test]
187 fn test_debug() {
188 let layout = TierLayout::Grid { columns: 2 };
189 let debug = format!("{layout:?}");
190 assert!(debug.contains("Grid"));
191 assert!(debug.contains("2"));
192 }
193
194 #[test]
195 fn test_clone_eq() {
196 let a = TierLayout::Grid { columns: 4 };
197 let b = a.clone();
198 assert_eq!(a, b);
199 }
200
201 #[test]
202 fn test_serde_auto_round_trip() {
203 let layout = TierLayout::Auto;
204 let json = serde_json::to_string(&layout).unwrap();
205 assert_eq!(json, "\"auto\"");
206 let deserialized: TierLayout = serde_json::from_str(&json).unwrap();
207 assert_eq!(layout, deserialized);
208 }
209
210 #[test]
211 fn test_yaml_empty_map_rejected() {
212 let yaml = "{}";
213 let result: Result<TierLayout, _> = serde_yml::from_str(yaml);
214 assert!(result.is_err());
215 }
216}