1use serde::{
4 Serialize,
5 ser::{SerializeMap, Serializer},
6};
7
8use super::Block;
9use super::anchor::Anchor;
10use super::inlines::{CalloutRef, InlineNode};
11use super::location::Location;
12use super::metadata::BlockMetadata;
13use super::title::Title;
14
15pub type ListLevel = u8;
16
17#[derive(Clone, Debug, PartialEq)]
19#[non_exhaustive]
20pub enum ListItemCheckedStatus {
21 Checked,
22 Unchecked,
23}
24
25#[derive(Clone, Debug, PartialEq)]
32#[non_exhaustive]
33pub struct ListItem {
34 pub level: ListLevel,
35 pub marker: String,
36 pub checked: Option<ListItemCheckedStatus>,
37 pub principal: Vec<InlineNode>,
39 pub blocks: Vec<Block>,
41 pub location: Location,
42}
43
44#[derive(Clone, Debug, PartialEq)]
46#[non_exhaustive]
47pub struct DescriptionList {
48 pub title: Title,
49 pub metadata: BlockMetadata,
50 pub items: Vec<DescriptionListItem>,
51 pub location: Location,
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize)]
76#[non_exhaustive]
77pub struct DescriptionListItem {
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
80 pub anchors: Vec<Anchor>,
81 #[serde(default, skip_serializing_if = "Vec::is_empty")]
83 pub term: Vec<InlineNode>,
84 pub delimiter: String,
86 #[serde(default, skip_serializing_if = "Vec::is_empty")]
88 pub principal_text: Vec<InlineNode>,
89 pub description: Vec<Block>,
91 pub location: Location,
92}
93
94#[derive(Clone, Debug, PartialEq)]
96#[non_exhaustive]
97pub struct UnorderedList {
98 pub title: Title,
99 pub metadata: BlockMetadata,
100 pub items: Vec<ListItem>,
101 pub marker: String,
102 pub location: Location,
103}
104
105#[derive(Clone, Debug, PartialEq)]
107#[non_exhaustive]
108pub struct OrderedList {
109 pub title: Title,
110 pub metadata: BlockMetadata,
111 pub items: Vec<ListItem>,
112 pub marker: String,
113 pub location: Location,
114}
115
116#[derive(Clone, Debug, PartialEq)]
120#[non_exhaustive]
121pub struct CalloutList {
122 pub title: Title,
123 pub metadata: BlockMetadata,
124 pub items: Vec<CalloutListItem>,
125 pub location: Location,
126}
127
128#[derive(Clone, Debug, PartialEq)]
140#[non_exhaustive]
141pub struct CalloutListItem {
142 pub callout: CalloutRef,
144 pub principal: Vec<InlineNode>,
146 pub blocks: Vec<Block>,
148 pub location: Location,
150}
151
152macro_rules! impl_list_serialize {
157 ($type:ty, $variant:literal, with_marker) => {
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 state.serialize_entry("marker", &self.marker)?;
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 };
180 ($type:ty, $variant:literal) => {
181 impl Serialize for $type {
182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183 where
184 S: Serializer,
185 {
186 let mut state = serializer.serialize_map(None)?;
187 state.serialize_entry("name", "list")?;
188 state.serialize_entry("type", "block")?;
189 state.serialize_entry("variant", $variant)?;
190 if !self.title.is_empty() {
191 state.serialize_entry("title", &self.title)?;
192 }
193 if !self.metadata.is_default() {
194 state.serialize_entry("metadata", &self.metadata)?;
195 }
196 state.serialize_entry("items", &self.items)?;
197 state.serialize_entry("location", &self.location)?;
198 state.end()
199 }
200 }
201 };
202}
203
204impl_list_serialize!(UnorderedList, "unordered", with_marker);
205impl_list_serialize!(OrderedList, "ordered", with_marker);
206impl_list_serialize!(CalloutList, "callout");
207
208impl Serialize for DescriptionList {
209 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
210 where
211 S: Serializer,
212 {
213 let mut state = serializer.serialize_map(None)?;
214 state.serialize_entry("name", "dlist")?;
215 state.serialize_entry("type", "block")?;
216 if !self.title.is_empty() {
217 state.serialize_entry("title", &self.title)?;
218 }
219 if !self.metadata.is_default() {
220 state.serialize_entry("metadata", &self.metadata)?;
221 }
222 state.serialize_entry("items", &self.items)?;
223 state.serialize_entry("location", &self.location)?;
224 state.end()
225 }
226}
227
228impl Serialize for ListItem {
229 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230 where
231 S: Serializer,
232 {
233 let mut state = serializer.serialize_map(None)?;
234 state.serialize_entry("name", "listItem")?;
235 state.serialize_entry("type", "block")?;
236 state.serialize_entry("marker", &self.marker)?;
237 if let Some(checked) = &self.checked {
238 state.serialize_entry("checked", checked)?;
239 }
240 state.serialize_entry("principal", &self.principal)?;
247 if !self.blocks.is_empty() {
248 state.serialize_entry("blocks", &self.blocks)?;
249 }
250 state.serialize_entry("location", &self.location)?;
251 state.end()
252 }
253}
254
255impl Serialize for ListItemCheckedStatus {
256 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257 where
258 S: Serializer,
259 {
260 match &self {
261 ListItemCheckedStatus::Checked => serializer.serialize_bool(true),
262 ListItemCheckedStatus::Unchecked => serializer.serialize_bool(false),
263 }
264 }
265}
266
267impl Serialize for CalloutListItem {
268 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269 where
270 S: Serializer,
271 {
272 let mut state = serializer.serialize_map(None)?;
273 state.serialize_entry("name", "listItem")?;
274 state.serialize_entry("type", "block")?;
275 state.serialize_entry("callout", &self.callout)?;
276 state.serialize_entry("principal", &self.principal)?;
277 if !self.blocks.is_empty() {
278 state.serialize_entry("blocks", &self.blocks)?;
279 }
280 state.serialize_entry("location", &self.location)?;
281 state.end()
282 }
283}