Skip to main content

cbor_edn/
transformable.rs

1use super::{
2    ApplicationLiteralsVisitor, DelimiterPolicy, Item, Sequence, StandaloneItem, TagVisitor,
3    TrailingNewlinePolicy, Visitor,
4};
5
6/// Trait that covers operations that are possible on a [`StandaloneItem`], a [`Sequence`] or an
7/// [`Item`].
8pub trait Transformable<'a>: sealed::Transformable<'a> {
9    /// Alters how space and comments are placed inside the item.
10    ///
11    /// See the policy values for details.
12    fn set_delimiters(&mut self, policy: DelimiterPolicy);
13
14    // FIXME: Those could be provided if sealed::Transformable had the visit function.
15
16    /// For each item in the tree that is a single application literal, call a callback.
17    ///
18    /// This is primarily used to apply custom EDN filtering:
19    ///
20    /// ```rust
21    /// # use cbor_edn::*;
22    /// let mut full = Sequence::parse("0 /unmodified/, german'zweiundvierzig'").unwrap();
23    /// full.visit_application_literals(&mut |id, value: String, item: &mut cbor_edn::Item| {
24    ///     if id == "german" {
25    ///         let numeric = match value.as_str() {
26    ///             "dreiundzwanzig" => 23,
27    ///             "zweiundvierzig" => 42,
28    ///             _ => todo!(),
29    ///         };
30    ///         *item = Item::new_integer_decimal(numeric).into();
31    ///     }
32    ///     Ok(())
33    /// });
34    /// assert_eq!(full.serialize(), "0 /unmodified/, 42");
35    /// ```
36    fn visit_application_literals<F>(&mut self, f: &mut F)
37    where
38        F: FnMut(String, String, &mut Item<'a>) -> Result<(), String> + ?Sized,
39    {
40        self.visit(&mut ApplicationLiteralsVisitor { user_fn: f });
41    }
42
43    /// For each item in the full tree (including embedded representations) that is tagged, call a
44    /// callback.
45    ///
46    /// Any error string is placed in a comment next to the item. The function should return Ok(())
47    /// on any tags it is not interested in visiting.
48    ///
49    /// This is primarily used to apply custom EDN application; see [crate::application::dt_tag_to_aol] for an
50    /// example.
51    fn visit_tag<F>(&mut self, f: &mut F)
52    where
53        F: FnMut(u64, &mut Item<'a>) -> Result<(), String> + ?Sized,
54    {
55        self.visit(&mut TagVisitor { user_fn: f });
56    }
57}
58
59pub(crate) mod sealed {
60    use super::*;
61
62    // Private methods go in here
63    pub trait Transformable<'a> {
64        fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
65        where
66            'a: 'b;
67
68        #[allow(private_bounds)] // reason: we're in a pub(crate) module and sealed::Transformable
69                                 // is not pub used
70        fn visit(&mut self, visitor: &mut impl Visitor<'a>);
71    }
72}
73
74impl<'a> sealed::Transformable<'a> for StandaloneItem<'a> {
75    fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
76    where
77        'a: 'b,
78    {
79        core::iter::once(self.item_mut())
80    }
81
82    #[allow(private_bounds)] // reason: see trait definition
83    fn visit(&mut self, visitor: &mut impl Visitor<'a>) {
84        self.1
85            .visit(visitor)
86            .use_space_before(&mut self.0)
87            .use_space_after(&mut self.2)
88            .done();
89    }
90}
91impl<'a> Transformable<'a> for StandaloneItem<'a> {
92    fn set_delimiters(&mut self, policy: DelimiterPolicy) {
93        // On the top level, let's not add the leading \n, because that would cause an empty line
94        // above the sole element formatted like this.
95        self.0.set_delimiters(policy, false);
96        self.1.set_delimiters(policy);
97        // FIXME: Does this also need the code from the Sequence cases?
98        self.2.set_delimiters(policy, true);
99    }
100}
101
102impl<'a> sealed::Transformable<'a> for Sequence<'a> {
103    fn toplevel_items<'b>(&'b mut self) -> impl Iterator<Item = &'b mut crate::Item<'a>>
104    where
105        'a: 'b,
106    {
107        self.items_mut()
108    }
109
110    #[allow(private_bounds)] // reason: see trait definition
111    fn visit(&mut self, visitor: &mut impl Visitor<'a>) {
112        if let Some(nmv) = self.items.as_mut() {
113            nmv.visit(visitor).use_space_after(&mut self.s0).done();
114        }
115    }
116}
117impl<'a> Transformable<'a> for Sequence<'a> {
118    fn set_delimiters(&mut self, policy: DelimiterPolicy) {
119        // On the top level, let's not add the leading \n, because that would cause an empty line
120        // above the sole element formatted like this.
121        self.s0.set_delimiters(policy, false);
122        if let Some(items) = self.items.as_mut() {
123            items.first.set_delimiters(policy);
124            for (msc, item) in items.tail.iter_mut() {
125                msc.set_delimiters(policy, true);
126                item.set_delimiters(policy);
127            }
128            match policy {
129                DelimiterPolicy::IndentedRegularSpacing {
130                    trailing_newline: TrailingNewlinePolicy::Always,
131                    ..
132                } => items.soc.set_delimiters(policy, true),
133                DelimiterPolicy::IndentedRegularSpacing {
134                    trailing_newline: TrailingNewlinePolicy::Never,
135                    ..
136                } => items.soc.set_delimiters(policy, false),
137                // FIXME: Should we be more explicit for the other cases, maybe even abandoning the
138                // 2nd argument?
139                _ => items.soc.set_delimiters(policy, !items.tail.is_empty()),
140            }
141        }
142        // else, I guess that even under TrailingNewlinePolicy::Always, not sending \n is OK
143        // because after all, we did end every line with a newline.
144    }
145}