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::{CalloutRef, 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<CalloutListItem>,
126 pub location: Location,
127}
128
129#[derive(Clone, Debug, PartialEq)]
141#[non_exhaustive]
142pub struct CalloutListItem {
143 pub callout: CalloutRef,
145 pub principal: Vec<InlineNode>,
147 pub blocks: Vec<Block>,
149 pub location: Location,
151}
152
153macro_rules! impl_list_serialize {
158 ($type:ty, $variant:literal, with_marker) => {
159 impl Serialize for $type {
160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 let mut state = serializer.serialize_map(None)?;
165 state.serialize_entry("name", "list")?;
166 state.serialize_entry("type", "block")?;
167 state.serialize_entry("variant", $variant)?;
168 state.serialize_entry("marker", &self.marker)?;
169 if !self.title.is_empty() {
170 state.serialize_entry("title", &self.title)?;
171 }
172 if !self.metadata.is_default() {
173 state.serialize_entry("metadata", &self.metadata)?;
174 }
175 state.serialize_entry("items", &self.items)?;
176 state.serialize_entry("location", &self.location)?;
177 state.end()
178 }
179 }
180 };
181 ($type:ty, $variant:literal) => {
182 impl Serialize for $type {
183 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184 where
185 S: Serializer,
186 {
187 let mut state = serializer.serialize_map(None)?;
188 state.serialize_entry("name", "list")?;
189 state.serialize_entry("type", "block")?;
190 state.serialize_entry("variant", $variant)?;
191 if !self.title.is_empty() {
192 state.serialize_entry("title", &self.title)?;
193 }
194 if !self.metadata.is_default() {
195 state.serialize_entry("metadata", &self.metadata)?;
196 }
197 state.serialize_entry("items", &self.items)?;
198 state.serialize_entry("location", &self.location)?;
199 state.end()
200 }
201 }
202 };
203}
204
205impl_list_serialize!(UnorderedList, "unordered", with_marker);
206impl_list_serialize!(OrderedList, "ordered", with_marker);
207impl_list_serialize!(CalloutList, "callout");
208
209impl Serialize for DescriptionList {
210 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
211 where
212 S: Serializer,
213 {
214 let mut state = serializer.serialize_map(None)?;
215 state.serialize_entry("name", "dlist")?;
216 state.serialize_entry("type", "block")?;
217 if !self.title.is_empty() {
218 state.serialize_entry("title", &self.title)?;
219 }
220 if !self.metadata.is_default() {
221 state.serialize_entry("metadata", &self.metadata)?;
222 }
223 state.serialize_entry("items", &self.items)?;
224 state.serialize_entry("location", &self.location)?;
225 state.end()
226 }
227}
228
229impl Serialize for ListItem {
230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231 where
232 S: Serializer,
233 {
234 let mut state = serializer.serialize_map(None)?;
235 state.serialize_entry("name", "listItem")?;
236 state.serialize_entry("type", "block")?;
237 state.serialize_entry("marker", &self.marker)?;
238 if let Some(checked) = &self.checked {
239 state.serialize_entry("checked", checked)?;
240 }
241 state.serialize_entry("principal", &self.principal)?;
248 if !self.blocks.is_empty() {
249 state.serialize_entry("blocks", &self.blocks)?;
250 }
251 state.serialize_entry("location", &self.location)?;
252 state.end()
253 }
254}
255
256impl Serialize for ListItemCheckedStatus {
257 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
258 where
259 S: Serializer,
260 {
261 match &self {
262 ListItemCheckedStatus::Checked => serializer.serialize_bool(true),
263 ListItemCheckedStatus::Unchecked => serializer.serialize_bool(false),
264 }
265 }
266}
267
268impl Serialize for CalloutListItem {
269 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
270 where
271 S: Serializer,
272 {
273 let mut state = serializer.serialize_map(None)?;
274 state.serialize_entry("name", "listItem")?;
275 state.serialize_entry("type", "block")?;
276 state.serialize_entry("callout", &self.callout)?;
277 state.serialize_entry("principal", &self.principal)?;
278 if !self.blocks.is_empty() {
279 state.serialize_entry("blocks", &self.blocks)?;
280 }
281 state.serialize_entry("location", &self.location)?;
282 state.end()
283 }
284}
285
286impl<'de> Deserialize<'de> for ListItem {
291 fn deserialize<D>(deserializer: D) -> Result<ListItem, D::Error>
292 where
293 D: Deserializer<'de>,
294 {
295 struct ListItemVisitor;
296
297 impl<'de> Visitor<'de> for ListItemVisitor {
298 type Value = ListItem;
299
300 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
301 formatter.write_str("a struct representing ListItem")
302 }
303
304 fn visit_map<V>(self, mut map: V) -> Result<ListItem, V::Error>
305 where
306 V: MapAccess<'de>,
307 {
308 let mut my_principal = None;
309 let mut my_blocks = None;
310 let mut my_checked = None;
311 let mut my_location = None;
312 let mut my_marker = None;
313
314 while let Some(key) = map.next_key::<String>()? {
315 match key.as_str() {
316 "principal" => {
317 if my_principal.is_some() {
318 return Err(de::Error::duplicate_field("principal"));
319 }
320 my_principal = Some(map.next_value()?);
321 }
322 "blocks" => {
323 if my_blocks.is_some() {
324 return Err(de::Error::duplicate_field("blocks"));
325 }
326 my_blocks = Some(map.next_value()?);
327 }
328 "marker" => {
329 if my_marker.is_some() {
330 return Err(de::Error::duplicate_field("marker"));
331 }
332 my_marker = Some(map.next_value::<String>()?);
333 }
334 "location" => {
335 if my_location.is_some() {
336 return Err(de::Error::duplicate_field("location"));
337 }
338 my_location = Some(map.next_value()?);
339 }
340 "checked" => {
341 if my_checked.is_some() {
342 return Err(de::Error::duplicate_field("checked"));
343 }
344 my_checked = Some(map.next_value::<bool>()?);
345 }
346 _ => {
347 tracing::debug!(?key, "ignoring unexpected field in ListItem");
348 let _ = map.next_value::<de::IgnoredAny>()?;
350 }
351 }
352 }
353 let marker = my_marker.ok_or_else(|| de::Error::missing_field("marker"))?;
354 let principal =
355 my_principal.ok_or_else(|| de::Error::missing_field("principal"))?;
356 let blocks = my_blocks.unwrap_or_default();
357 let level =
358 ListLevel::try_from(ListItem::parse_depth_from_marker(&marker).unwrap_or(1))
359 .map_err(|e| {
360 de::Error::custom(format!("invalid list item level from marker: {e}",))
361 })?;
362 let checked = my_checked.map(|c| {
363 if c {
364 ListItemCheckedStatus::Checked
365 } else {
366 ListItemCheckedStatus::Unchecked
367 }
368 });
369 Ok(ListItem {
370 level,
371 marker,
372 location: my_location.ok_or_else(|| de::Error::missing_field("location"))?,
373 principal,
374 blocks,
375 checked,
376 })
377 }
378 }
379 deserializer.deserialize_map(ListItemVisitor)
380 }
381}
382
383impl<'de> Deserialize<'de> for ListItemCheckedStatus {
384 fn deserialize<D>(deserializer: D) -> Result<ListItemCheckedStatus, D::Error>
385 where
386 D: serde::Deserializer<'de>,
387 {
388 struct ListItemCheckedStatusVisitor;
389
390 impl Visitor<'_> for ListItemCheckedStatusVisitor {
391 type Value = ListItemCheckedStatus;
392
393 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
394 formatter.write_str("a boolean representing checked status")
395 }
396
397 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
398 where
399 E: de::Error,
400 {
401 if v {
402 Ok(ListItemCheckedStatus::Checked)
403 } else {
404 Ok(ListItemCheckedStatus::Unchecked)
405 }
406 }
407 }
408
409 deserializer.deserialize_bool(ListItemCheckedStatusVisitor)
410 }
411}
412
413impl<'de> Deserialize<'de> for CalloutListItem {
414 fn deserialize<D>(deserializer: D) -> Result<CalloutListItem, D::Error>
415 where
416 D: Deserializer<'de>,
417 {
418 struct CalloutListItemVisitor;
419
420 impl<'de> Visitor<'de> for CalloutListItemVisitor {
421 type Value = CalloutListItem;
422
423 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
424 formatter.write_str("a struct representing CalloutListItem")
425 }
426
427 fn visit_map<V>(self, mut map: V) -> Result<CalloutListItem, V::Error>
428 where
429 V: MapAccess<'de>,
430 {
431 let mut my_callout = None;
432 let mut my_principal = None;
433 let mut my_blocks = None;
434 let mut my_location = None;
435
436 while let Some(key) = map.next_key::<String>()? {
437 match key.as_str() {
438 "callout" => {
439 if my_callout.is_some() {
440 return Err(de::Error::duplicate_field("callout"));
441 }
442 my_callout = Some(map.next_value()?);
443 }
444 "principal" => {
445 if my_principal.is_some() {
446 return Err(de::Error::duplicate_field("principal"));
447 }
448 my_principal = Some(map.next_value()?);
449 }
450 "blocks" => {
451 if my_blocks.is_some() {
452 return Err(de::Error::duplicate_field("blocks"));
453 }
454 my_blocks = Some(map.next_value()?);
455 }
456 "location" => {
457 if my_location.is_some() {
458 return Err(de::Error::duplicate_field("location"));
459 }
460 my_location = Some(map.next_value()?);
461 }
462 _ => {
463 tracing::debug!(?key, "ignoring unexpected field in CalloutListItem");
464 let _ = map.next_value::<de::IgnoredAny>()?;
465 }
466 }
467 }
468
469 Ok(CalloutListItem {
470 callout: my_callout.ok_or_else(|| de::Error::missing_field("callout"))?,
471 principal: my_principal.ok_or_else(|| de::Error::missing_field("principal"))?,
472 blocks: my_blocks.unwrap_or_default(),
473 location: my_location.ok_or_else(|| de::Error::missing_field("location"))?,
474 })
475 }
476 }
477 deserializer.deserialize_map(CalloutListItemVisitor)
478 }
479}