cbor_edn/
visitor.rs

1//! Visitor building blocks.
2//!
3//! A [`Visitor`] is an object that is called, in sequence, with many components of an CBOR item or
4//! sequence tree.
5//!
6//! This makes sense not only for the mere purpose of abstraction (things worked fine and
7//! maintainably in earlier versions), but also serves to enable influence on the environment:
8//! Processing something (eg. an EDN [`Item`][crate::Item]) may result in an effect on the container (eg. when
9//! processing an item fails, and the error should be shown in a comment next to it -- but comments
10//! are part of the space between items, and need to be applied there by the container; made harder
11//! in Rust where during iteration, both the previous and the next item might want to write into
12//! the same space).
13//!
14//! Right now, the visitor only gets to process items. Other things (such as space or
15//! NonemptyMscVec lists) could be made processable when needed.
16
17pub(crate) trait Visitor {
18    /// This function gets called for every item by the various visitors.
19    ///
20    /// If the action can not be performed on the item in a self-contained way, it may bubble up a
21    /// non-default [`ProcessResult`].
22    fn process(&mut self, _item: &mut crate::Item<'_>) -> ProcessResult {
23        ProcessResult::default()
24    }
25}
26
27/// Description of actions to take around the item.
28///
29/// This describes only actions that can not be done by acting on a [`&mut crate::Item`] itself;
30/// for example, wrapping it in a tag or replacing it with an application oriented literal is not a
31/// [`ProcessResult`], but an action taken in [`Visitor::process`].
32#[must_use]
33#[derive(PartialEq)]
34pub(crate) struct ProcessResult {
35    comments_after: Vec<String>,
36    recurse: bool,
37}
38
39impl Default for ProcessResult {
40    fn default() -> Self {
41        Self {
42            comments_after: vec![],
43            recurse: true,
44        }
45    }
46}
47
48/// # Building a `ProcessResult`
49impl ProcessResult {
50    pub(crate) fn with_comment_after(mut self, comment: impl Into<String>) -> Self {
51        self.comments_after.push(comment.into());
52        self
53    }
54
55    /// Configure the visitation to not recurse into this item.
56    ///
57    /// This might be a bit atypical of visitors, and one might think that direct access to a top
58    /// level item's members (array elements or map key-value pairs) would work better, but those
59    /// accessors can not act on the iteration item's surroundings, while a visitor can.
60    pub(crate) fn stop_recursion(self) -> Self {
61        Self {
62            recurse: false,
63            ..self
64        }
65    }
66
67    pub(crate) fn chain(self, other: Self) -> Self {
68        Self {
69            recurse: self.recurse && other.recurse,
70            comments_after: self
71                .comments_after
72                .into_iter()
73                .chain(other.comments_after)
74                .collect(),
75        }
76    }
77}
78
79/// # Consuming a `ProcessResult`
80///
81/// These methods are called by recipients: they work off all the actionables by calling the
82/// `.to_…` functions with suitable recipients of the actions, ending with [`.done()`][Self::done].
83impl ProcessResult {
84    // If we typestated a bit more, we could avoid this (if use_space_before ->
85    // ProcessResultHalfProcessed)
86    #[track_caller]
87    pub(crate) fn done(self) {
88        if self != Default::default() {
89            panic!(
90                "use_space_before() and use_space_after() have been called, this should be empty."
91            );
92        }
93    }
94
95    pub(crate) fn take_recurse(&mut self) -> bool {
96        let recurse = self.recurse;
97        // FIXME: It'd be prettier if we could reliably detect this and not just when it deviates
98        // from the default, but that'd need explicit setting in a process result.
99        self.recurse = true;
100        recurse
101    }
102
103    pub(crate) fn use_space_before(self, _s: &mut impl crate::space::Spaceish) -> Self {
104        self
105    }
106
107    pub(crate) fn use_space_after(mut self, s: &mut impl crate::space::Spaceish) -> Self {
108        for comment in self.comments_after.drain(..) {
109            s.prepend_comment(&comment);
110        }
111        self
112    }
113}
114
115pub(crate) struct ApplicationLiteralsVisitor<F> {
116    pub(crate) user_fn: F,
117}
118
119impl<F: for<'b> FnMut(String, String, &mut crate::Item<'b>) -> Result<(), String>> Visitor
120    for ApplicationLiteralsVisitor<F>
121{
122    fn process(&mut self, item: &mut crate::Item<'_>) -> ProcessResult {
123        let Ok((identifier, value)) = item.get_application_literal() else {
124            return ProcessResult::default();
125        };
126        match (self.user_fn)(identifier, value, item) {
127            Ok(()) => ProcessResult::default(),
128            Err(e) => ProcessResult::default().with_comment_after(e),
129        }
130    }
131}
132
133pub(crate) struct TagVisitor<F> {
134    pub(crate) user_fn: F,
135}
136
137impl<F: for<'b> FnMut(u64, &mut crate::Item<'b>) -> Result<(), String>> Visitor for TagVisitor<F> {
138    fn process(&mut self, item: &mut crate::Item<'_>) -> ProcessResult {
139        let Ok(tag) = item.get_tag() else {
140            return ProcessResult::default();
141        };
142        match (self.user_fn)(tag, item) {
143            Ok(()) => ProcessResult::default(),
144            Err(e) => ProcessResult::default().with_comment_after(e),
145        }
146    }
147}
148
149pub(crate) struct ArrayElementVisitor<F> {
150    seen_first: bool,
151    user_fn: F,
152}
153
154impl<F> ArrayElementVisitor<F> {
155    pub(crate) fn new(user_fn: F) -> Self {
156        Self {
157            seen_first: false,
158            user_fn,
159        }
160    }
161}
162
163impl<F: for<'b> FnMut(&mut crate::Item<'b>) -> Result<Option<String>, String>> Visitor
164    for ArrayElementVisitor<F>
165{
166    fn process(&mut self, item: &mut crate::Item<'_>) -> ProcessResult {
167        if !self.seen_first {
168            // We don't really iterate, just checking if it is an array
169            if item.get_array_items_mut().is_err() {
170                return ProcessResult::default()
171                    .with_comment_after("Expected array".to_string())
172                    .stop_recursion();
173            }
174            self.seen_first = true;
175            return ProcessResult::default();
176        }
177
178        match (self.user_fn)(item) {
179            // We currently don't distinguish between comment and error in the output (but may
180            // later)
181            Ok(Some(text)) => ProcessResult::default().with_comment_after(text),
182            Ok(None) => ProcessResult::default(),
183            Err(e) => ProcessResult::default().with_comment_after(e),
184        }
185        .stop_recursion()
186    }
187}
188
189pub(crate) struct MapElementVisitor<F> {
190    seen_first: bool,
191    /// Handler to be applied to the next map value.
192    ///
193    /// Visiting a key sets this to `Some(_)` (containing whatever the user_fn gave for the key,
194    /// which may be `None`), visiting the next item (which is the value) takes that and leaves
195    /// `None`.
196    next_value_handler: Option<Option<MapValueHandler>>,
197    user_fn: F,
198}
199
200impl<F> MapElementVisitor<F> {
201    pub(crate) fn new(user_fn: F) -> Self {
202        Self {
203            seen_first: false,
204            next_value_handler: None,
205            user_fn,
206        }
207    }
208}
209
210pub type MapValueHandler =
211    Box<dyn for<'data> FnOnce(&mut crate::Item<'data>) -> Result<Option<String>, String>>;
212
213impl<
214        F: for<'b> FnMut(
215            &mut crate::Item<'b>,
216        ) -> Result<(Option<String>, Option<MapValueHandler>), String>,
217    > Visitor for MapElementVisitor<F>
218{
219    fn process(&mut self, item: &mut crate::Item<'_>) -> ProcessResult {
220        if !self.seen_first {
221            // We don't really iterate, just checking if it is an array
222            if item.get_map_items().is_err() {
223                return ProcessResult::default()
224                    .with_comment_after("Expected map".to_string())
225                    .stop_recursion();
226            }
227            self.seen_first = true;
228            return ProcessResult::default();
229        }
230
231        if let Some(handler) = self.next_value_handler.take() {
232            if let Some(handler) = handler {
233                match (handler)(item) {
234                    // We currently don't distinguish between comment and error in the output (but may
235                    // later)
236                    Ok(Some(text)) => ProcessResult::default().with_comment_after(text),
237                    Ok(None) => ProcessResult::default(),
238                    Err(e) => ProcessResult::default().with_comment_after(e),
239                }
240            } else {
241                Default::default()
242            }
243        } else {
244            match (self.user_fn)(item) {
245                // We currently don't distinguish between comment and error in the output (but may
246                // later)
247                Ok((comment, value_handler)) => {
248                    self.next_value_handler = Some(value_handler);
249                    match comment {
250                        Some(text) => ProcessResult::default().with_comment_after(text),
251                        None => ProcessResult::default(),
252                    }
253                }
254                Err(e) => ProcessResult::default().with_comment_after(e),
255            }
256        }
257        .stop_recursion()
258    }
259}