1 | use crate::InternalString; |
2 | |
3 | /// Opaque string storage for raw TOML; internal to `toml_edit` |
4 | #[derive (PartialEq, Eq, Clone, Hash)] |
5 | pub struct RawString(RawStringInner); |
6 | |
7 | #[derive (PartialEq, Eq, Clone, Hash)] |
8 | enum RawStringInner { |
9 | Empty, |
10 | Explicit(InternalString), |
11 | Spanned(std::ops::Range<usize>), |
12 | } |
13 | |
14 | impl 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 | |
111 | impl Default for RawString { |
112 | fn default() -> Self { |
113 | Self(RawStringInner::Empty) |
114 | } |
115 | } |
116 | |
117 | impl 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: &InternalString) => write!(formatter, " {s:?}" ), |
123 | RawStringInner::Spanned(s: &Range) => write!(formatter, " {s:?}" ), |
124 | } |
125 | } |
126 | } |
127 | |
128 | impl 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 | |
139 | impl 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 | |
150 | impl 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 | |
161 | impl From<InternalString> for RawString { |
162 | #[inline ] |
163 | fn from(inner: InternalString) -> Self { |
164 | Self(RawStringInner::Explicit(inner)) |
165 | } |
166 | } |
167 | |
168 | impl 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 | |
179 | impl 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 | } |
189 | |