1use serde::{
4 Deserialize, Serialize,
5 de::{self, Deserializer, MapAccess, Visitor},
6 ser::{SerializeMap, Serializer},
7};
8
9use super::Block;
10use super::anchor::Anchor;
11use super::inlines::InlineNode;
12use super::location::Location;
13use super::metadata::BlockMetadata;
14use super::title::Title;
15
16pub type ListLevel = u8;
17
18#[derive(Clone, Debug, PartialEq)]
20#[non_exhaustive]
21pub enum ListItemCheckedStatus {
22 Checked,
23 Unchecked,
24}
25
26#[derive(Clone, Debug, PartialEq)]
33#[non_exhaustive]
34pub struct ListItem {
35 pub level: ListLevel,
36 pub marker: String,
37 pub checked: Option<ListItemCheckedStatus>,
38 pub principal: Vec<InlineNode>,
40 pub blocks: Vec<Block>,
42 pub location: Location,
43}
44
45#[derive(Clone, Debug, PartialEq)]
47#[non_exhaustive]
48pub struct DescriptionList {
49 pub title: Title,
50 pub metadata: BlockMetadata,
51 pub items: Vec<DescriptionListItem>,
52 pub location: Location,
53}
54
55#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
77#[non_exhaustive]
78pub struct DescriptionListItem {
79 #[serde(default, skip_serializing_if = "Vec::is_empty")]
81 pub anchors: Vec<Anchor>,
82 #[serde(default, skip_serializing_if = "Vec::is_empty")]
84 pub term: Vec<InlineNode>,
85 pub delimiter: String,
87 #[serde(default, skip_serializing_if = "Vec::is_empty")]
89 pub principal_text: Vec<InlineNode>,
90 pub description: Vec<Block>,
92 pub location: Location,
93}
94
95#[derive(Clone, Debug, PartialEq)]
97#[non_exhaustive]
98pub struct UnorderedList {
99 pub title: Title,
100 pub metadata: BlockMetadata,
101 pub items: Vec<ListItem>,
102 pub marker: String,
103 pub location: Location,
104}
105
106#[derive(Clone, Debug, PartialEq)]
108#[non_exhaustive]
109pub struct OrderedList {
110 pub title: Title,
111 pub metadata: BlockMetadata,
112 pub items: Vec<ListItem>,
113 pub marker: String,
114 pub location: Location,
115}
116
117#[derive(Clone, Debug, PartialEq)]
121#[non_exhaustive]
122pub struct CalloutList {
123 pub title: Title,
124 pub metadata: BlockMetadata,
125 pub items: Vec<ListItem>,
126 pub location: Location,
127}
128
129macro_rules! impl_list_serialize {
134 ($type:ty, $variant:literal, with_marker) => {
135 impl Serialize for $type {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: Serializer,
139 {
140 let mut state = serializer.serialize_map(None)?;
141 state.serialize_entry("name", "list")?;
142 state.serialize_entry("type", "block")?;
143 state.serialize_entry("variant", $variant)?;
144 state.serialize_entry("marker", &self.marker)?;
145 if !self.title.is_empty() {
146 state.serialize_entry("title", &self.title)?;
147 }
148 if !self.metadata.is_default() {
149 state.serialize_entry("metadata", &self.metadata)?;
150 }
151 state.serialize_entry("items", &self.items)?;
152 state.serialize_entry("location", &self.location)?;
153 state.end()
154 }
155 }
156 };
157 ($type:ty, $variant:literal) => {
158 impl Serialize for $type {
159 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
160 where
161 S: Serializer,
162 {
163 let mut state = serializer.serialize_map(None)?;
164 state.serialize_entry("name", "list")?;
165 state.serialize_entry("type", "block")?;
166 state.serialize_entry("variant", $variant)?;
167 if !self.title.is_empty() {
168 state.serialize_entry("title", &self.title)?;
169 }
170 if !self.metadata.is_default() {
171 state.serialize_entry("metadata", &self.metadata)?;
172 }
173 state.serialize_entry("items", &self.items)?;
174 state.serialize_entry("location", &self.location)?;
175 state.end()
176 }
177 }
178 };
179}
180
181impl_list_serialize!(UnorderedList, "unordered", with_marker);
182impl_list_serialize!(OrderedList, "ordered", with_marker);
183impl_list_serialize!(CalloutList, "callout");
184
185impl Serialize for DescriptionList {
186 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
187 where
188 S: Serializer,
189 {
190 let mut state = serializer.serialize_map(None)?;
191 state.serialize_entry("name", "dlist")?;
192 state.serialize_entry("type", "block")?;
193 if !self.title.is_empty() {
194 state.serialize_entry("title", &self.title)?;
195 }
196 if !self.metadata.is_default() {
197 state.serialize_entry("metadata", &self.metadata)?;
198 }
199 state.serialize_entry("items", &self.items)?;
200 state.serialize_entry("location", &self.location)?;
201 state.end()
202 }
203}
204
205impl Serialize for ListItem {
206 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
207 where
208 S: Serializer,
209 {
210 let mut state = serializer.serialize_map(None)?;
211 state.serialize_entry("name", "listItem")?;
212 state.serialize_entry("type", "block")?;
213 state.serialize_entry("marker", &self.marker)?;
214 if let Some(checked) = &self.checked {
215 state.serialize_entry("checked", checked)?;
216 }
217 state.serialize_entry("principal", &self.principal)?;
224 if !self.blocks.is_empty() {
225 state.serialize_entry("blocks", &self.blocks)?;
226 }
227 state.serialize_entry("location", &self.location)?;
228 state.end()
229 }
230}
231
232impl Serialize for ListItemCheckedStatus {
233 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
234 where
235 S: Serializer,
236 {
237 match &self {
238 ListItemCheckedStatus::Checked => serializer.serialize_bool(true),
239 ListItemCheckedStatus::Unchecked => serializer.serialize_bool(false),
240 }
241 }
242}
243
244impl<'de> Deserialize<'de> for ListItem {
249 fn deserialize<D>(deserializer: D) -> Result<ListItem, D::Error>
250 where
251 D: Deserializer<'de>,
252 {
253 struct ListItemVisitor;
254
255 impl<'de> Visitor<'de> for ListItemVisitor {
256 type Value = ListItem;
257
258 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
259 formatter.write_str("a struct representing ListItem")
260 }
261
262 fn visit_map<V>(self, mut map: V) -> Result<ListItem, V::Error>
263 where
264 V: MapAccess<'de>,
265 {
266 let mut my_principal = None;
267 let mut my_blocks = None;
268 let mut my_checked = None;
269 let mut my_location = None;
270 let mut my_marker = None;
271
272 while let Some(key) = map.next_key::<String>()? {
273 match key.as_str() {
274 "principal" => {
275 if my_principal.is_some() {
276 return Err(de::Error::duplicate_field("principal"));
277 }
278 my_principal = Some(map.next_value()?);
279 }
280 "blocks" => {
281 if my_blocks.is_some() {
282 return Err(de::Error::duplicate_field("blocks"));
283 }
284 my_blocks = Some(map.next_value()?);
285 }
286 "marker" => {
287 if my_marker.is_some() {
288 return Err(de::Error::duplicate_field("marker"));
289 }
290 my_marker = Some(map.next_value::<String>()?);
291 }
292 "location" => {
293 if my_location.is_some() {
294 return Err(de::Error::duplicate_field("location"));
295 }
296 my_location = Some(map.next_value()?);
297 }
298 "checked" => {
299 if my_checked.is_some() {
300 return Err(de::Error::duplicate_field("checked"));
301 }
302 my_checked = Some(map.next_value::<bool>()?);
303 }
304 _ => {
305 tracing::debug!(?key, "ignoring unexpected field in ListItem");
306 let _ = map.next_value::<de::IgnoredAny>()?;
308 }
309 }
310 }
311 let marker = my_marker.ok_or_else(|| de::Error::missing_field("marker"))?;
312 let principal =
313 my_principal.ok_or_else(|| de::Error::missing_field("principal"))?;
314 let blocks = my_blocks.unwrap_or_default();
315 let level =
316 ListLevel::try_from(ListItem::parse_depth_from_marker(&marker).unwrap_or(1))
317 .map_err(|e| {
318 de::Error::custom(format!("invalid list item level from marker: {e}",))
319 })?;
320 let checked = my_checked.map(|c| {
321 if c {
322 ListItemCheckedStatus::Checked
323 } else {
324 ListItemCheckedStatus::Unchecked
325 }
326 });
327 Ok(ListItem {
328 level,
329 marker,
330 location: my_location.ok_or_else(|| de::Error::missing_field("location"))?,
331 principal,
332 blocks,
333 checked,
334 })
335 }
336 }
337 deserializer.deserialize_map(ListItemVisitor)
338 }
339}
340
341impl<'de> Deserialize<'de> for ListItemCheckedStatus {
342 fn deserialize<D>(deserializer: D) -> Result<ListItemCheckedStatus, D::Error>
343 where
344 D: serde::Deserializer<'de>,
345 {
346 struct ListItemCheckedStatusVisitor;
347
348 impl Visitor<'_> for ListItemCheckedStatusVisitor {
349 type Value = ListItemCheckedStatus;
350
351 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
352 formatter.write_str("a boolean representing checked status")
353 }
354
355 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
356 where
357 E: de::Error,
358 {
359 if v {
360 Ok(ListItemCheckedStatus::Checked)
361 } else {
362 Ok(ListItemCheckedStatus::Unchecked)
363 }
364 }
365 }
366
367 deserializer.deserialize_bool(ListItemCheckedStatusVisitor)
368 }
369}