vimwiki_core/lang/elements/blocks/lists/
mod.rs

1use crate::{
2    lang::elements::{
3        AsChildrenMutSlice, AsChildrenSlice, Element, InlineBlockElement,
4        InlineElement, InlineElementContainer, IntoChildren, Located,
5    },
6    StrictEq,
7};
8use derive_more::{
9    AsRef, Constructor, Deref, DerefMut, From, Index, IndexMut, Into,
10    IntoIterator,
11};
12use serde::{Deserialize, Serialize};
13use std::iter::FromIterator;
14
15mod item;
16pub use item::*;
17
18/// Represents a regular list comprised of individual items
19#[derive(
20    Constructor,
21    Clone,
22    Debug,
23    From,
24    Eq,
25    PartialEq,
26    Index,
27    IndexMut,
28    IntoIterator,
29    Serialize,
30    Deserialize,
31)]
32pub struct List<'a> {
33    /// Represents items contained within the list
34    #[index]
35    #[index_mut]
36    #[into_iterator(owned, ref, ref_mut)]
37    pub items: Vec<Located<ListItem<'a>>>,
38}
39
40impl List<'_> {
41    pub fn to_borrowed(&self) -> List {
42        self.into_iter()
43            .map(|x| x.as_ref().map(ListItem::to_borrowed))
44            .collect()
45    }
46
47    pub fn into_owned(self) -> List<'static> {
48        self.into_iter()
49            .map(|x| x.map(ListItem::into_owned))
50            .collect()
51    }
52}
53
54impl<'a> List<'a> {
55    /// Returns iterator of references to list items
56    pub fn iter(&self) -> impl Iterator<Item = &Located<ListItem<'a>>> {
57        self.into_iter()
58    }
59
60    /// Returns iterator of mutable references to list items
61    pub fn iter_mut(
62        &mut self,
63    ) -> impl Iterator<Item = &mut Located<ListItem<'a>>> {
64        self.into_iter()
65    }
66
67    /// Returns total items contained in list
68    pub fn len(&self) -> usize {
69        self.items.len()
70    }
71
72    /// Returns true if list has no items
73    pub fn is_empty(&self) -> bool {
74        self.items.is_empty()
75    }
76
77    /// Returns whether or not the list represents an ordered list based on
78    /// the first list item; if there are no items then this would return false
79    pub fn is_ordered(&self) -> bool {
80        self.iter()
81            .next()
82            .map_or(false, |item| item.ty.is_ordered())
83    }
84
85    /// Returns whether or not the list represents an unordered list based on
86    /// the first list item; if there are no items then this would return false
87    pub fn is_unordered(&self) -> bool {
88        self.iter()
89            .next()
90            .map_or(false, |item| item.ty.is_unordered())
91    }
92
93    /// Normalizes the list by standardizing the item types based on the
94    /// first list item.
95    ///
96    /// For example, if you have the following list:
97    ///
98    /// 1. Hyphen
99    /// 2. Number
100    /// 3. Asterisk
101    ///
102    /// You would get back out the following list:
103    ///
104    /// 1. Hyphen
105    /// 2. Hyphen
106    /// 3. Hyphen
107    ///
108    /// Note that this does NOT normalize sublists, which should be done
109    /// manually.
110    pub(crate) fn normalize(&mut self) -> &mut Self {
111        // If we have items, we want to go through and normalize their types
112        if let [head, tail @ ..] = &mut self.items[..] {
113            // TODO: Need to support special case where not all item types are
114            //       roman numeral but the first one is, as this can happen with
115            //       alphabetic lists if for some reason starting with i and moving
116            //       on to other letters like j and k
117            for item in tail {
118                item.ty = head.ty.clone();
119            }
120        }
121
122        self
123    }
124}
125
126impl<'a> AsChildrenSlice for List<'a> {
127    type Child = Located<ListItem<'a>>;
128
129    fn as_children_slice(&self) -> &[Self::Child] {
130        &self.items
131    }
132}
133
134impl<'a> AsChildrenMutSlice for List<'a> {
135    type Child = Located<ListItem<'a>>;
136
137    fn as_children_mut_slice(&mut self) -> &mut [Self::Child] {
138        &mut self.items
139    }
140}
141
142impl<'a> IntoChildren for List<'a> {
143    type Child = Located<InlineBlockElement<'a>>;
144
145    fn into_children(self) -> Vec<Self::Child> {
146        self.into_iter()
147            .map(|x| x.map(InlineBlockElement::from))
148            .collect()
149    }
150}
151
152impl<'a> FromIterator<Located<ListItem<'a>>> for List<'a> {
153    fn from_iter<I: IntoIterator<Item = Located<ListItem<'a>>>>(
154        iter: I,
155    ) -> Self {
156        Self::new(iter.into_iter().collect())
157    }
158}
159
160impl<'a> StrictEq for List<'a> {
161    /// Performs a strict_eq check against list items
162    fn strict_eq(&self, other: &Self) -> bool {
163        self.items.strict_eq(&other.items)
164    }
165}
166
167/// Represents some content associated with a list item, either being
168/// an inline element or a new sublist
169#[derive(Clone, Debug, From, Eq, PartialEq, Serialize, Deserialize)]
170pub enum ListItemContent<'a> {
171    InlineContent(InlineElementContainer<'a>),
172    List(List<'a>),
173}
174
175impl ListItemContent<'_> {
176    pub fn to_borrowed(&self) -> ListItemContent {
177        match self {
178            Self::InlineContent(ref x) => {
179                ListItemContent::from(x.to_borrowed())
180            }
181            Self::List(ref x) => ListItemContent::from(x.to_borrowed()),
182        }
183    }
184
185    pub fn into_owned(self) -> ListItemContent<'static> {
186        match self {
187            Self::InlineContent(x) => ListItemContent::from(x.into_owned()),
188            Self::List(x) => ListItemContent::from(x.into_owned()),
189        }
190    }
191}
192
193impl<'a> StrictEq for ListItemContent<'a> {
194    /// Performs a strict_eq check against eqivalent variants
195    fn strict_eq(&self, other: &Self) -> bool {
196        match (self, other) {
197            (Self::InlineContent(x), Self::InlineContent(y)) => x.strict_eq(y),
198            (Self::List(x), Self::List(y)) => x.strict_eq(y),
199            _ => false,
200        }
201    }
202}
203
204/// Represents a collection of list item content
205#[derive(
206    AsRef,
207    Constructor,
208    Clone,
209    Debug,
210    Default,
211    Deref,
212    DerefMut,
213    Index,
214    IndexMut,
215    Into,
216    IntoIterator,
217    Eq,
218    PartialEq,
219    Serialize,
220    Deserialize,
221)]
222#[as_ref(forward)]
223#[into_iterator(owned, ref, ref_mut)]
224pub struct ListItemContents<'a>(Vec<Located<ListItemContent<'a>>>);
225
226impl ListItemContents<'_> {
227    pub fn to_borrowed(&self) -> ListItemContents {
228        self.iter()
229            .map(|x| x.as_ref().map(ListItemContent::to_borrowed))
230            .collect()
231    }
232
233    pub fn into_owned(self) -> ListItemContents<'static> {
234        self.into_iter()
235            .map(|x| x.map(ListItemContent::into_owned))
236            .collect()
237    }
238}
239
240impl<'a> ListItemContents<'a> {
241    pub fn inline_content_iter(
242        &self,
243    ) -> impl Iterator<Item = &InlineElement> + '_ {
244        self.iter()
245            .filter_map(|c| match c.as_inner() {
246                ListItemContent::InlineContent(x) => {
247                    Some(x.iter().map(|y| y.as_inner()))
248                }
249                _ => None,
250            })
251            .flatten()
252    }
253
254    pub fn inline_content_iter_mut(
255        &mut self,
256    ) -> impl Iterator<Item = &mut InlineElement<'a>> + '_ {
257        self.iter_mut()
258            .filter_map(|c| match c.as_mut_inner() {
259                ListItemContent::InlineContent(x) => {
260                    Some(x.iter_mut().map(|y| y.as_mut_inner()))
261                }
262                _ => None,
263            })
264            .flatten()
265    }
266
267    pub fn sublist_iter(&self) -> impl Iterator<Item = &List> + '_ {
268        self.iter().flat_map(|c| match c.as_inner() {
269            ListItemContent::List(x) => Some(x),
270            _ => None,
271        })
272    }
273
274    pub fn sublist_iter_mut(
275        &mut self,
276    ) -> impl Iterator<Item = &mut List<'a>> + '_ {
277        self.iter_mut().flat_map(|c| match c.as_mut_inner() {
278            ListItemContent::List(x) => Some(x),
279            _ => None,
280        })
281    }
282}
283
284impl<'a> AsChildrenSlice for ListItemContents<'a> {
285    type Child = Located<ListItemContent<'a>>;
286
287    fn as_children_slice(&self) -> &[Self::Child] {
288        &self.0
289    }
290}
291
292impl<'a> AsChildrenMutSlice for ListItemContents<'a> {
293    type Child = Located<ListItemContent<'a>>;
294
295    fn as_children_mut_slice(&mut self) -> &mut [Self::Child] {
296        &mut self.0
297    }
298}
299
300impl<'a> IntoChildren for ListItemContents<'a> {
301    type Child = Located<Element<'a>>;
302
303    fn into_children(self) -> Vec<Self::Child> {
304        self.into_iter()
305            .flat_map(|x| {
306                let region = x.region();
307                match x.into_inner() {
308                    ListItemContent::InlineContent(content) => content
309                        .into_children()
310                        .into_iter()
311                        .map(|x| x.map(Element::from))
312                        .collect(),
313                    ListItemContent::List(list) => {
314                        vec![Located::new(Element::from(list), region)]
315                    }
316                }
317            })
318            .collect()
319    }
320}
321
322impl<'a> FromIterator<Located<ListItemContent<'a>>> for ListItemContents<'a> {
323    fn from_iter<I: IntoIterator<Item = Located<ListItemContent<'a>>>>(
324        iter: I,
325    ) -> Self {
326        Self::new(iter.into_iter().collect())
327    }
328}
329
330impl<'a> StrictEq for ListItemContents<'a> {
331    /// Performs a strict_eq check against inner contents
332    fn strict_eq(&self, other: &Self) -> bool {
333        self.0.strict_eq(&other.0)
334    }
335}