mdq/select/
sel_list_item.rs

1use crate::md_elem::elem::List;
2use crate::md_elem::{MdContext, MdElem};
3use crate::select::string_matcher::StringMatcher;
4use crate::select::{ListItemMatcher, ListItemTask, TrySelector};
5
6#[derive(Debug, PartialEq)]
7pub struct ListItemSelector {
8    li_type: ListItemType,
9    checkbox: ListItemTask,
10    string_matcher: StringMatcher,
11}
12
13impl From<ListItemMatcher> for ListItemSelector {
14    fn from(value: ListItemMatcher) -> Self {
15        Self {
16            li_type: if value.ordered {
17                ListItemType::Ordered
18            } else {
19                ListItemType::Unordered
20            },
21            checkbox: value.task,
22            string_matcher: value.matcher.into(),
23        }
24    }
25}
26
27#[derive(Debug, PartialEq, Copy, Clone)]
28pub enum ListItemType {
29    Ordered,
30    Unordered,
31}
32
33fn task_matches(matcher: ListItemTask, md_is_checked: Option<bool>) -> bool {
34    match matcher {
35        ListItemTask::Unselected => md_is_checked == Some(false),
36        ListItemTask::Selected => md_is_checked == Some(true),
37        ListItemTask::Either => md_is_checked.is_some(),
38        ListItemTask::None => md_is_checked.is_none(),
39    }
40}
41
42impl TrySelector<List> for ListItemSelector {
43    fn try_select(&self, _: &MdContext, item: List) -> Result<Vec<MdElem>, MdElem> {
44        // This one works a bit differently than most:
45        // - If the item has a single list, check it; this is essentially a recursive base case.
46        // - Otherwise, never match, but return an MdElem::Doc of the list items, each as its own list.
47        //   That way, the find_children code in api.rs will recurse back into this method for each of those items, but
48        //   as a single-item list for the base case.
49        let List { starting_index, items } = item;
50        match items.as_slice() {
51            [li] => {
52                let matched = self.li_type.matches(&starting_index)
53                    && task_matches(self.checkbox, li.checked)
54                    && self.string_matcher.matches_any(&li.item);
55                let list = MdElem::List(List { starting_index, items });
56                if matched {
57                    Ok(vec![list])
58                } else {
59                    Err(list)
60                }
61            }
62            _ => {
63                let mut idx = starting_index;
64                let mut items_doc = Vec::with_capacity(items.len());
65                for item in items {
66                    items_doc.push(MdElem::List(List {
67                        starting_index: idx,
68                        items: vec![item],
69                    }));
70                    if let Some(idx) = idx.as_mut() {
71                        *idx += 1;
72                    }
73                }
74                Err(MdElem::Doc(items_doc))
75            }
76        }
77    }
78}
79
80impl ListItemType {
81    fn matches(&self, idx: &Option<u32>) -> bool {
82        match self {
83            ListItemType::Ordered => idx.is_some(),
84            ListItemType::Unordered => idx.is_none(),
85        }
86    }
87}