toml_edit/
raw_string.rs

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