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)]
57#[non_exhaustive]
58pub struct DescriptionListItem {
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
60 pub anchors: Vec<Anchor>,
61 #[serde(default, skip_serializing_if = "Vec::is_empty")]
62 pub term: Vec<InlineNode>,
63 pub delimiter: String,
64 #[serde(default, skip_serializing_if = "Vec::is_empty")]
65 pub principal_text: Vec<InlineNode>,
66 pub description: Vec<Block>,
67 pub location: Location,
68}
69
70#[derive(Clone, Debug, PartialEq)]
72#[non_exhaustive]
73pub struct UnorderedList {
74 pub title: Title,
75 pub metadata: BlockMetadata,
76 pub items: Vec<ListItem>,
77 pub marker: String,
78 pub location: Location,
79}
80
81#[derive(Clone, Debug, PartialEq)]
83#[non_exhaustive]
84pub struct OrderedList {
85 pub title: Title,
86 pub metadata: BlockMetadata,
87 pub items: Vec<ListItem>,
88 pub marker: String,
89 pub location: Location,
90}
91
92#[derive(Clone, Debug, PartialEq)]
96#[non_exhaustive]
97pub struct CalloutList {
98 pub title: Title,
99 pub metadata: BlockMetadata,
100 pub items: Vec<ListItem>,
101 pub location: Location,
102}
103
104macro_rules! impl_list_serialize {
109 ($type:ty, $variant:literal, with_marker) => {
110 impl Serialize for $type {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: Serializer,
114 {
115 let mut state = serializer.serialize_map(None)?;
116 state.serialize_entry("name", "list")?;
117 state.serialize_entry("type", "block")?;
118 state.serialize_entry("variant", $variant)?;
119 state.serialize_entry("marker", &self.marker)?;
120 if !self.title.is_empty() {
121 state.serialize_entry("title", &self.title)?;
122 }
123 if !self.metadata.is_default() {
124 state.serialize_entry("metadata", &self.metadata)?;
125 }
126 state.serialize_entry("items", &self.items)?;
127 state.serialize_entry("location", &self.location)?;
128 state.end()
129 }
130 }
131 };
132 ($type:ty, $variant:literal) => {
133 impl Serialize for $type {
134 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: Serializer,
137 {
138 let mut state = serializer.serialize_map(None)?;
139 state.serialize_entry("name", "list")?;
140 state.serialize_entry("type", "block")?;
141 state.serialize_entry("variant", $variant)?;
142 if !self.title.is_empty() {
143 state.serialize_entry("title", &self.title)?;
144 }
145 if !self.metadata.is_default() {
146 state.serialize_entry("metadata", &self.metadata)?;
147 }
148 state.serialize_entry("items", &self.items)?;
149 state.serialize_entry("location", &self.location)?;
150 state.end()
151 }
152 }
153 };
154}
155
156impl_list_serialize!(UnorderedList, "unordered", with_marker);
157impl_list_serialize!(OrderedList, "ordered", with_marker);
158impl_list_serialize!(CalloutList, "callout");
159
160impl Serialize for DescriptionList {
161 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162 where
163 S: Serializer,
164 {
165 let mut state = serializer.serialize_map(None)?;
166 state.serialize_entry("name", "dlist")?;
167 state.serialize_entry("type", "block")?;
168 if !self.title.is_empty() {
169 state.serialize_entry("title", &self.title)?;
170 }
171 if !self.metadata.is_default() {
172 state.serialize_entry("metadata", &self.metadata)?;
173 }
174 state.serialize_entry("items", &self.items)?;
175 state.serialize_entry("location", &self.location)?;
176 state.end()
177 }
178}
179
180impl Serialize for ListItem {
181 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182 where
183 S: Serializer,
184 {
185 let mut state = serializer.serialize_map(None)?;
186 state.serialize_entry("name", "listItem")?;
187 state.serialize_entry("type", "block")?;
188 state.serialize_entry("marker", &self.marker)?;
189 if let Some(checked) = &self.checked {
190 state.serialize_entry("checked", checked)?;
191 }
192 state.serialize_entry("principal", &self.principal)?;
199 if !self.blocks.is_empty() {
200 state.serialize_entry("blocks", &self.blocks)?;
201 }
202 state.serialize_entry("location", &self.location)?;
203 state.end()
204 }
205}
206
207impl Serialize for ListItemCheckedStatus {
208 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209 where
210 S: Serializer,
211 {
212 match &self {
213 ListItemCheckedStatus::Checked => serializer.serialize_bool(true),
214 ListItemCheckedStatus::Unchecked => serializer.serialize_bool(false),
215 }
216 }
217}
218
219impl<'de> Deserialize<'de> for ListItem {
224 fn deserialize<D>(deserializer: D) -> Result<ListItem, D::Error>
225 where
226 D: Deserializer<'de>,
227 {
228 struct ListItemVisitor;
229
230 impl<'de> Visitor<'de> for ListItemVisitor {
231 type Value = ListItem;
232
233 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
234 formatter.write_str("a struct representing ListItem")
235 }
236
237 fn visit_map<V>(self, mut map: V) -> Result<ListItem, V::Error>
238 where
239 V: MapAccess<'de>,
240 {
241 let mut my_principal = None;
242 let mut my_blocks = None;
243 let mut my_checked = None;
244 let mut my_location = None;
245 let mut my_marker = None;
246
247 while let Some(key) = map.next_key::<String>()? {
248 match key.as_str() {
249 "principal" => {
250 if my_principal.is_some() {
251 return Err(de::Error::duplicate_field("principal"));
252 }
253 my_principal = Some(map.next_value()?);
254 }
255 "blocks" => {
256 if my_blocks.is_some() {
257 return Err(de::Error::duplicate_field("blocks"));
258 }
259 my_blocks = Some(map.next_value()?);
260 }
261 "marker" => {
262 if my_marker.is_some() {
263 return Err(de::Error::duplicate_field("marker"));
264 }
265 my_marker = Some(map.next_value::<String>()?);
266 }
267 "location" => {
268 if my_location.is_some() {
269 return Err(de::Error::duplicate_field("location"));
270 }
271 my_location = Some(map.next_value()?);
272 }
273 "checked" => {
274 if my_checked.is_some() {
275 return Err(de::Error::duplicate_field("checked"));
276 }
277 my_checked = Some(map.next_value::<bool>()?);
278 }
279 _ => {
280 tracing::debug!(?key, "ignoring unexpected field in ListItem");
281 let _ = map.next_value::<de::IgnoredAny>()?;
283 }
284 }
285 }
286 let marker = my_marker.ok_or_else(|| de::Error::missing_field("marker"))?;
287 let principal =
288 my_principal.ok_or_else(|| de::Error::missing_field("principal"))?;
289 let blocks = my_blocks.unwrap_or_default();
290 let level =
291 ListLevel::try_from(ListItem::parse_depth_from_marker(&marker).unwrap_or(1))
292 .map_err(|e| {
293 de::Error::custom(format!("invalid list item level from marker: {e}",))
294 })?;
295 let checked = my_checked.map(|c| {
296 if c {
297 ListItemCheckedStatus::Checked
298 } else {
299 ListItemCheckedStatus::Unchecked
300 }
301 });
302 Ok(ListItem {
303 level,
304 marker,
305 location: my_location.ok_or_else(|| de::Error::missing_field("location"))?,
306 principal,
307 blocks,
308 checked,
309 })
310 }
311 }
312 deserializer.deserialize_map(ListItemVisitor)
313 }
314}
315
316impl<'de> Deserialize<'de> for ListItemCheckedStatus {
317 fn deserialize<D>(deserializer: D) -> Result<ListItemCheckedStatus, D::Error>
318 where
319 D: serde::Deserializer<'de>,
320 {
321 struct ListItemCheckedStatusVisitor;
322
323 impl Visitor<'_> for ListItemCheckedStatusVisitor {
324 type Value = ListItemCheckedStatus;
325
326 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
327 formatter.write_str("a boolean representing checked status")
328 }
329
330 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
331 where
332 E: de::Error,
333 {
334 if v {
335 Ok(ListItemCheckedStatus::Checked)
336 } else {
337 Ok(ListItemCheckedStatus::Unchecked)
338 }
339 }
340 }
341
342 deserializer.deserialize_bool(ListItemCheckedStatusVisitor)
343 }
344}