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