Skip to main content

mxp/element/
decoder.rs

1use std::borrow::Cow;
2use std::iter::FusedIterator;
3use std::slice;
4
5use super::action::Action;
6use super::element::Element;
7use super::item::ElementItem;
8use crate::arguments::Arguments;
9use crate::element::AttributeList;
10use crate::entity::DecodedEntity;
11use crate::parse::Decoder;
12
13/// This struct is created by [`Element::decoder`](crate::element::Element::decoder).
14/// See its documentation for more.
15#[derive(Copy, Clone, Debug)]
16pub struct ElementDecoder<'a, D: Decoder> {
17    decoder: D,
18    attributes: &'a AttributeList,
19    args: &'a Arguments<'a>,
20}
21
22impl<'a, D: Decoder> ElementDecoder<'a, D> {
23    pub(super) fn new(decoder: D, attributes: &'a AttributeList, args: &'a Arguments<'a>) -> Self {
24        Self {
25            decoder,
26            attributes,
27            args,
28        }
29    }
30}
31
32impl<'a, D: Decoder> ElementDecoder<'a, D> {
33    fn find(&self, name: &str) -> Option<&'a str> {
34        self.attributes.find(name, self.args)
35    }
36}
37
38impl<D: Decoder> Decoder for ElementDecoder<'_, D> {
39    fn get_entity(&self, name: &str) -> Option<&str> {
40        self.find(name).or_else(|| self.decoder.get_entity(name))
41    }
42
43    fn decode_entity(&self, name: &str) -> crate::Result<DecodedEntity<'_>> {
44        match self.find(name) {
45            Some(attr) => Ok(self.decoder.decode_string(attr)?.into()),
46            None => self.decoder.decode_entity(name),
47        }
48    }
49}
50
51/// This struct is created by [`Element::decode`](crate::element::Element::decode).
52/// See its documentation for more.
53#[must_use = "iterators are lazy and do nothing unless consumed"]
54#[derive(Clone)]
55pub struct ElementDecodeIter<'a, D: Decoder + Copy> {
56    decoder: ElementDecoder<'a, D>,
57    items: slice::Iter<'a, ElementItem>,
58}
59
60impl<'a, D: Decoder + Copy> ElementDecodeIter<'a, D> {
61    pub(super) fn new(element: &'a Element, args: &'a Arguments<&'a str>, decoder: D) -> Self {
62        Self {
63            decoder: ElementDecoder {
64                decoder,
65                attributes: &element.attributes,
66                args,
67            },
68            items: element.items.iter(),
69        }
70    }
71}
72
73impl<'a, D: Decoder + Copy> Iterator for ElementDecodeIter<'a, D> {
74    type Item = crate::Result<Action<Cow<'a, str>>>;
75
76    fn next(&mut self) -> Option<Self::Item> {
77        Some(self.items.next()?.decode(self.decoder))
78    }
79
80    fn size_hint(&self) -> (usize, Option<usize>) {
81        let exact = self.len();
82        (exact, Some(exact))
83    }
84}
85
86impl<D> ExactSizeIterator for ElementDecodeIter<'_, D>
87where
88    D: Decoder + Copy,
89{
90    fn len(&self) -> usize {
91        self.items.len()
92    }
93}
94
95impl<D> FusedIterator for ElementDecodeIter<'_, D> where D: Decoder + Copy {}
96
97#[cfg(test)]
98mod tests {
99    use crate::color::RgbColor;
100    use crate::element::Action;
101    use crate::elements::Color;
102    use crate::test_utils::{decode_actions, try_from_node};
103
104    fn color(name: &str) -> RgbColor {
105        RgbColor::named(name).unwrap()
106    }
107
108    fn foreground(name: &str) -> Color {
109        Color {
110            fore: Some(color(name)),
111            ..Default::default()
112        }
113    }
114
115    #[test]
116    fn custom_args() {
117        let mut state = crate::State::default();
118        let definition = try_from_node("<!ELEMENT mycolor '<COLOR &col;>' ATT='col=red'>");
119        state.define(definition).unwrap();
120        let red = decode_actions("<mycolor>", &state).unwrap();
121        assert_eq!(red, &[Action::Color(foreground("red"))]);
122        let blue = decode_actions("<mycolor col=blue>", &state).unwrap();
123        assert_eq!(blue, &[Action::Color(foreground("blue"))]);
124        let reset = decode_actions("<mycolor col=\"\">", &state).unwrap();
125        assert_eq!(reset, &[Action::Color(Color::default())]);
126
127        let definition = try_from_node("<!ELEMENT mycolors '<COLOR &col; &bg;>' ATT='bg col=red'>");
128        state.define(definition).unwrap();
129        let positional = decode_actions("<mycolors col=blue green>", &state).unwrap();
130        assert_eq!(
131            positional,
132            &[Action::Color(Color {
133                fore: Some(color("blue")),
134                back: Some(color("green")),
135            })]
136        );
137    }
138}