1use std::borrow::Cow;
2
3use crate::RawString;
4
5/// A value together with its `to_string` representation,
6/// including surrounding it whitespaces and comments.
7#[derive(Eq, PartialEq, Clone, Hash)]
8pub struct Formatted<T> {
9 value: T,
10 repr: Option<Repr>,
11 decor: Decor,
12}
13
14impl<T> Formatted<T>
15where
16 T: ValueRepr,
17{
18 /// Default-formatted value
19 pub fn new(value: T) -> Self {
20 Self {
21 value,
22 repr: None,
23 decor: Default::default(),
24 }
25 }
26
27 pub(crate) fn set_repr_unchecked(&mut self, repr: Repr) {
28 self.repr = Some(repr);
29 }
30
31 /// The wrapped value
32 pub fn value(&self) -> &T {
33 &self.value
34 }
35
36 /// The wrapped value
37 pub fn into_value(self) -> T {
38 self.value
39 }
40
41 /// Returns the raw representation, if available.
42 pub fn as_repr(&self) -> Option<&Repr> {
43 self.repr.as_ref()
44 }
45
46 /// Returns the default raw representation.
47 pub fn default_repr(&self) -> Repr {
48 self.value.to_repr()
49 }
50
51 /// Returns a raw representation.
52 pub fn display_repr(&self) -> Cow<str> {
53 self.as_repr()
54 .and_then(|r| r.as_raw().as_str())
55 .map(Cow::Borrowed)
56 .unwrap_or_else(|| {
57 Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
58 })
59 }
60
61 /// Returns the location within the original document
62 pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
63 self.repr.as_ref().and_then(|r| r.span())
64 }
65
66 pub(crate) fn despan(&mut self, input: &str) {
67 self.decor.despan(input);
68 if let Some(repr) = &mut self.repr {
69 repr.despan(input);
70 }
71 }
72
73 /// Returns the surrounding whitespace
74 pub fn decor_mut(&mut self) -> &mut Decor {
75 &mut self.decor
76 }
77
78 /// Returns the surrounding whitespace
79 pub fn decor(&self) -> &Decor {
80 &self.decor
81 }
82
83 /// Auto formats the value.
84 pub fn fmt(&mut self) {
85 self.repr = Some(self.value.to_repr());
86 }
87}
88
89impl<T> std::fmt::Debug for Formatted<T>
90where
91 T: std::fmt::Debug,
92{
93 #[inline]
94 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
95 let mut d: DebugStruct<'_, '_> = formatter.debug_struct(name:"Formatted");
96 d.field(name:"value", &self.value);
97 match &self.repr {
98 Some(r: &Repr) => d.field(name:"repr", value:r),
99 None => d.field(name:"repr", &"default"),
100 };
101 d.field(name:"decor", &self.decor);
102 d.finish()
103 }
104}
105
106impl<T> std::fmt::Display for Formatted<T>
107where
108 T: ValueRepr,
109{
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 crate::encode::Encode::encode(self, buf:f, input:None, ("", ""))
112 }
113}
114
115pub trait ValueRepr: crate::private::Sealed {
116 /// The TOML representation of the value
117 fn to_repr(&self) -> Repr;
118}
119
120/// TOML-encoded value
121#[derive(Eq, PartialEq, Clone, Hash)]
122pub struct Repr {
123 raw_value: RawString,
124}
125
126impl Repr {
127 pub(crate) fn new_unchecked(raw: impl Into<RawString>) -> Self {
128 Repr {
129 raw_value: raw.into(),
130 }
131 }
132
133 /// Access the underlying value
134 pub fn as_raw(&self) -> &RawString {
135 &self.raw_value
136 }
137
138 /// Returns the location within the original document
139 pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
140 self.raw_value.span()
141 }
142
143 pub(crate) fn despan(&mut self, input: &str) {
144 self.raw_value.despan(input)
145 }
146
147 pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
148 self.as_raw().encode(buf, input)
149 }
150}
151
152impl std::fmt::Debug for Repr {
153 #[inline]
154 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
155 self.raw_value.fmt(formatter)
156 }
157}
158
159/// A prefix and suffix,
160///
161/// Including comments, whitespaces and newlines.
162#[derive(Eq, PartialEq, Clone, Default, Hash)]
163pub struct Decor {
164 prefix: Option<RawString>,
165 suffix: Option<RawString>,
166}
167
168impl Decor {
169 /// Creates a new decor from the given prefix and suffix.
170 pub fn new(prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
171 Self {
172 prefix: Some(prefix.into()),
173 suffix: Some(suffix.into()),
174 }
175 }
176
177 /// Go back to default decor
178 pub fn clear(&mut self) {
179 self.prefix = None;
180 self.suffix = None;
181 }
182
183 /// Get the prefix.
184 pub fn prefix(&self) -> Option<&RawString> {
185 self.prefix.as_ref()
186 }
187
188 pub(crate) fn prefix_encode(
189 &self,
190 buf: &mut dyn std::fmt::Write,
191 input: Option<&str>,
192 default: &str,
193 ) -> std::fmt::Result {
194 if let Some(prefix) = self.prefix() {
195 prefix.encode_with_default(buf, input, default)
196 } else {
197 write!(buf, "{}", default)
198 }
199 }
200
201 /// Set the prefix.
202 pub fn set_prefix(&mut self, prefix: impl Into<RawString>) {
203 self.prefix = Some(prefix.into());
204 }
205
206 /// Get the suffix.
207 pub fn suffix(&self) -> Option<&RawString> {
208 self.suffix.as_ref()
209 }
210
211 pub(crate) fn suffix_encode(
212 &self,
213 buf: &mut dyn std::fmt::Write,
214 input: Option<&str>,
215 default: &str,
216 ) -> std::fmt::Result {
217 if let Some(suffix) = self.suffix() {
218 suffix.encode_with_default(buf, input, default)
219 } else {
220 write!(buf, "{}", default)
221 }
222 }
223
224 /// Set the suffix.
225 pub fn set_suffix(&mut self, suffix: impl Into<RawString>) {
226 self.suffix = Some(suffix.into());
227 }
228
229 pub(crate) fn despan(&mut self, input: &str) {
230 if let Some(prefix) = &mut self.prefix {
231 prefix.despan(input);
232 }
233 if let Some(suffix) = &mut self.suffix {
234 suffix.despan(input);
235 }
236 }
237}
238
239impl std::fmt::Debug for Decor {
240 #[inline]
241 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
242 let mut d: DebugStruct<'_, '_> = formatter.debug_struct(name:"Decor");
243 match &self.prefix {
244 Some(r: &RawString) => d.field(name:"prefix", value:r),
245 None => d.field(name:"prefix", &"default"),
246 };
247 match &self.suffix {
248 Some(r: &RawString) => d.field(name:"suffix", value:r),
249 None => d.field(name:"suffix", &"default"),
250 };
251 d.finish()
252 }
253}
254