toml_edit/
repr.rs

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    #[cfg(feature = "display")]
48    pub fn default_repr(&self) -> Repr {
49        self.value.to_repr()
50    }
51
52    /// Returns a raw representation.
53    #[cfg(feature = "display")]
54    pub fn display_repr(&self) -> Cow<'_, str> {
55        self.as_repr()
56            .and_then(|r| r.as_raw().as_str())
57            .map(Cow::Borrowed)
58            .unwrap_or_else(|| {
59                Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
60            })
61    }
62
63    /// The location within the original document
64    ///
65    /// This generally requires an [`ImDocument`][crate::ImDocument].
66    pub fn span(&self) -> Option<std::ops::Range<usize>> {
67        self.repr.as_ref().and_then(|r| r.span())
68    }
69
70    pub(crate) fn despan(&mut self, input: &str) {
71        self.decor.despan(input);
72        if let Some(repr) = &mut self.repr {
73            repr.despan(input);
74        }
75    }
76
77    /// Returns the surrounding whitespace
78    pub fn decor_mut(&mut self) -> &mut Decor {
79        &mut self.decor
80    }
81
82    /// Returns the surrounding whitespace
83    pub fn decor(&self) -> &Decor {
84        &self.decor
85    }
86
87    /// Auto formats the value.
88    pub fn fmt(&mut self) {
89        self.repr = None;
90    }
91}
92
93impl<T> std::fmt::Debug for Formatted<T>
94where
95    T: std::fmt::Debug,
96{
97    #[inline]
98    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
99        let mut d = formatter.debug_struct("Formatted");
100        d.field("value", &self.value);
101        match &self.repr {
102            Some(r) => d.field("repr", r),
103            None => d.field("repr", &"default"),
104        };
105        d.field("decor", &self.decor);
106        d.finish()
107    }
108}
109
110#[cfg(feature = "display")]
111impl<T> std::fmt::Display for Formatted<T>
112where
113    T: ValueRepr,
114{
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        crate::encode::encode_formatted(self, f, None, ("", ""))
117    }
118}
119
120pub trait ValueRepr: crate::private::Sealed {
121    /// The TOML representation of the value
122    #[cfg(feature = "display")]
123    fn to_repr(&self) -> Repr;
124}
125
126#[cfg(not(feature = "display"))]
127mod inner {
128    use super::ValueRepr;
129
130    impl ValueRepr for String {}
131    impl ValueRepr for i64 {}
132    impl ValueRepr for f64 {}
133    impl ValueRepr for bool {}
134    impl ValueRepr for toml_datetime::Datetime {}
135}
136
137/// TOML-encoded value
138#[derive(Eq, PartialEq, Clone, Hash)]
139pub struct Repr {
140    raw_value: RawString,
141}
142
143impl Repr {
144    pub(crate) fn new_unchecked(raw: impl Into<RawString>) -> Self {
145        Repr {
146            raw_value: raw.into(),
147        }
148    }
149
150    /// Access the underlying value
151    pub fn as_raw(&self) -> &RawString {
152        &self.raw_value
153    }
154
155    /// The location within the original document
156    ///
157    /// This generally requires an [`ImDocument`][crate::ImDocument].
158    pub fn span(&self) -> Option<std::ops::Range<usize>> {
159        self.raw_value.span()
160    }
161
162    pub(crate) fn despan(&mut self, input: &str) {
163        self.raw_value.despan(input);
164    }
165
166    #[cfg(feature = "display")]
167    pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
168        self.as_raw().encode(buf, input)
169    }
170}
171
172impl std::fmt::Debug for Repr {
173    #[inline]
174    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
175        self.raw_value.fmt(formatter)
176    }
177}
178
179/// A prefix and suffix,
180///
181/// Including comments, whitespaces and newlines.
182#[derive(Eq, PartialEq, Clone, Default, Hash)]
183pub struct Decor {
184    prefix: Option<RawString>,
185    suffix: Option<RawString>,
186}
187
188impl Decor {
189    /// Creates a new decor from the given prefix and suffix.
190    pub fn new(prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
191        Self {
192            prefix: Some(prefix.into()),
193            suffix: Some(suffix.into()),
194        }
195    }
196
197    /// Go back to default decor
198    pub fn clear(&mut self) {
199        self.prefix = None;
200        self.suffix = None;
201    }
202
203    /// Get the prefix.
204    pub fn prefix(&self) -> Option<&RawString> {
205        self.prefix.as_ref()
206    }
207
208    #[cfg(feature = "display")]
209    pub(crate) fn prefix_encode(
210        &self,
211        buf: &mut dyn std::fmt::Write,
212        input: Option<&str>,
213        default: &str,
214    ) -> std::fmt::Result {
215        if let Some(prefix) = self.prefix() {
216            prefix.encode_with_default(buf, input, default)
217        } else {
218            write!(buf, "{default}")
219        }
220    }
221
222    /// Set the prefix.
223    pub fn set_prefix(&mut self, prefix: impl Into<RawString>) {
224        self.prefix = Some(prefix.into());
225    }
226
227    /// Get the suffix.
228    pub fn suffix(&self) -> Option<&RawString> {
229        self.suffix.as_ref()
230    }
231
232    #[cfg(feature = "display")]
233    pub(crate) fn suffix_encode(
234        &self,
235        buf: &mut dyn std::fmt::Write,
236        input: Option<&str>,
237        default: &str,
238    ) -> std::fmt::Result {
239        if let Some(suffix) = self.suffix() {
240            suffix.encode_with_default(buf, input, default)
241        } else {
242            write!(buf, "{default}")
243        }
244    }
245
246    /// Set the suffix.
247    pub fn set_suffix(&mut self, suffix: impl Into<RawString>) {
248        self.suffix = Some(suffix.into());
249    }
250
251    pub(crate) fn despan(&mut self, input: &str) {
252        if let Some(prefix) = &mut self.prefix {
253            prefix.despan(input);
254        }
255        if let Some(suffix) = &mut self.suffix {
256            suffix.despan(input);
257        }
258    }
259}
260
261impl std::fmt::Debug for Decor {
262    #[inline]
263    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
264        let mut d = formatter.debug_struct("Decor");
265        match &self.prefix {
266            Some(r) => d.field("prefix", r),
267            None => d.field("prefix", &"default"),
268        };
269        match &self.suffix {
270            Some(r) => d.field("suffix", r),
271            None => d.field("suffix", &"default"),
272        };
273        d.finish()
274    }
275}