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        if span.start == span.end {
17            RawString(RawStringInner::Empty)
18        } else {
19            RawString(RawStringInner::Spanned(span))
20        }
21    }
22
23    /// Access the underlying string
24    ///
25    /// This generally requires a [`DocumentMut`][crate::DocumentMut].
26    pub fn as_str(&self) -> Option<&str> {
27        match &self.0 {
28            RawStringInner::Empty => Some(""),
29            RawStringInner::Explicit(s) => Some(s.as_str()),
30            RawStringInner::Spanned(_) => None,
31        }
32    }
33
34    /// The location within the original document
35    ///
36    /// This generally requires an [`ImDocument`][crate::ImDocument].
37    pub fn span(&self) -> Option<std::ops::Range<usize>> {
38        match &self.0 {
39            RawStringInner::Empty => None,
40            RawStringInner::Explicit(_) => None,
41            RawStringInner::Spanned(span) => Some(span.clone()),
42        }
43    }
44
45    pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
46        match &self.0 {
47            RawStringInner::Empty => "",
48            RawStringInner::Explicit(s) => s.as_str(),
49            RawStringInner::Spanned(span) => input
50                .get(span.clone())
51                .unwrap_or_else(|| panic!("span {span:?} should be in input:\n```\n{input}\n```")),
52        }
53    }
54
55    pub(crate) fn to_str_with_default<'s>(
56        &'s self,
57        input: Option<&'s str>,
58        default: &'s str,
59    ) -> &'s str {
60        match &self.0 {
61            RawStringInner::Empty => "",
62            RawStringInner::Explicit(s) => s.as_str(),
63            RawStringInner::Spanned(span) => {
64                if let Some(input) = input {
65                    input.get(span.clone()).unwrap_or_else(|| {
66                        panic!("span {span:?} should be in input:\n```\n{input}\n```")
67                    })
68                } else {
69                    default
70                }
71            }
72        }
73    }
74
75    pub(crate) fn despan(&mut self, input: &str) {
76        match &self.0 {
77            RawStringInner::Empty => {}
78            RawStringInner::Explicit(_) => {}
79            RawStringInner::Spanned(span) => {
80                *self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
81                    panic!("span {span:?} should be in input:\n```\n{input}\n```")
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}