toml_edit/
raw_string.rs

1/// Opaque string storage for raw TOML; internal to `toml_edit`
2#[derive(PartialEq, Eq, Clone, Hash)]
3pub struct RawString(RawStringInner);
4
5#[derive(PartialEq, Eq, Clone, Hash)]
6enum RawStringInner {
7    Empty,
8    Explicit(String),
9    Spanned(std::ops::Range<usize>),
10}
11
12impl RawString {
13    pub(crate) fn with_span(span: std::ops::Range<usize>) -> Self {
14        Self(RawStringInner::Spanned(span))
15    }
16
17    /// Access the underlying string
18    ///
19    /// This generally requires a [`DocumentMut`][crate::DocumentMut].
20    pub fn as_str(&self) -> Option<&str> {
21        match &self.0 {
22            RawStringInner::Empty => Some(""),
23            RawStringInner::Explicit(s) => Some(s.as_str()),
24            RawStringInner::Spanned(_) => None,
25        }
26    }
27
28    /// The location within the original document
29    ///
30    /// This generally requires a [`Document`][crate::Document].
31    pub fn span(&self) -> Option<std::ops::Range<usize>> {
32        match &self.0 {
33            RawStringInner::Empty => None,
34            RawStringInner::Explicit(_) => None,
35            RawStringInner::Spanned(span) => Some(span.clone()),
36        }
37    }
38
39    pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
40        match &self.0 {
41            RawStringInner::Empty => "",
42            RawStringInner::Explicit(s) => s.as_str(),
43            RawStringInner::Spanned(span) => input
44                .get(span.clone())
45                .unwrap_or_else(|| panic!("span {span:?} should be in input:\n```\n{input}\n```")),
46        }
47    }
48
49    pub(crate) fn to_str_with_default<'s>(
50        &'s self,
51        input: Option<&'s str>,
52        default: &'s str,
53    ) -> &'s str {
54        match &self.0 {
55            RawStringInner::Empty => "",
56            RawStringInner::Explicit(s) => s.as_str(),
57            RawStringInner::Spanned(span) => {
58                if let Some(input) = input {
59                    input.get(span.clone()).unwrap_or_else(|| {
60                        panic!("span {span:?} should be in input:\n```\n{input}\n```")
61                    })
62                } else {
63                    default
64                }
65            }
66        }
67    }
68
69    pub(crate) fn despan(&mut self, input: &str) {
70        match &self.0 {
71            RawStringInner::Empty => {}
72            RawStringInner::Explicit(_) => {}
73            RawStringInner::Spanned(span) => {
74                if span.start == span.end {
75                    *self = Self(RawStringInner::Empty);
76                } else {
77                    *self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
78                        panic!("span {span:?} should be in input:\n```\n{input}\n```")
79                    }));
80                }
81            }
82        }
83    }
84
85    #[cfg(feature = "display")]
86    pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
87        let raw = self.to_str(input);
88        for part in raw.split('\r') {
89            write!(buf, "{part}")?;
90        }
91        Ok(())
92    }
93
94    #[cfg(feature = "display")]
95    pub(crate) fn encode_with_default(
96        &self,
97        buf: &mut dyn std::fmt::Write,
98        input: Option<&str>,
99        default: &str,
100    ) -> std::fmt::Result {
101        let raw = self.to_str_with_default(input, default);
102        for part in raw.split('\r') {
103            write!(buf, "{part}")?;
104        }
105        Ok(())
106    }
107}
108
109impl Default for RawString {
110    fn default() -> Self {
111        Self(RawStringInner::Empty)
112    }
113}
114
115impl std::fmt::Debug for RawString {
116    #[inline]
117    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
118        match &self.0 {
119            RawStringInner::Empty => write!(formatter, "empty"),
120            RawStringInner::Explicit(s) => write!(formatter, "{s:?}"),
121            RawStringInner::Spanned(s) => write!(formatter, "{s:?}"),
122        }
123    }
124}
125
126impl From<&str> for RawString {
127    #[inline]
128    fn from(s: &str) -> Self {
129        if s.is_empty() {
130            Self(RawStringInner::Empty)
131        } else {
132            String::from(s).into()
133        }
134    }
135}
136
137impl From<String> for RawString {
138    #[inline]
139    fn from(s: String) -> Self {
140        if s.is_empty() {
141            Self(RawStringInner::Empty)
142        } else {
143            Self(RawStringInner::Explicit(s))
144        }
145    }
146}
147
148impl From<&String> for RawString {
149    #[inline]
150    fn from(s: &String) -> Self {
151        if s.is_empty() {
152            Self(RawStringInner::Empty)
153        } else {
154            String::from(s).into()
155        }
156    }
157}
158
159impl From<Box<str>> for RawString {
160    #[inline]
161    fn from(s: Box<str>) -> Self {
162        if s.is_empty() {
163            Self(RawStringInner::Empty)
164        } else {
165            String::from(s).into()
166        }
167    }
168}