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}