1use {
5 crate::{ArenaData, ArenaIdx, StrChunk},
6 arcstr::Substr,
7 std::{borrow::Cow, sync::Arc},
8};
9
10#[derive(PartialEq, Eq, Clone, Hash, Debug)]
13#[must_use = "Ast output must not be discarded"]
14pub struct AstStr(ArenaIdx<Inner>);
15
16impl AstStr {
17 pub fn new(repr: impl Into<AstStrRepr>) -> Self {
19 Self(
20 ArenaIdx::new(Inner { repr: repr.into(), input: None, })
21 )
22 }
23
24 pub fn from_input(input: Substr) -> Self {
26 Self(ArenaIdx::new(Inner {
27 repr: AstStrRepr::String(Substr::clone(&input)),
28 input: Some(input),
29 }))
30 }
31
32 pub fn replace(&mut self, repr: impl Into<AstStrRepr>) {
34 self.0.repr = repr.into();
35 }
36
37 pub fn join(self, other: Self) -> Self {
39 let input = match (&self.0.input, &other.0.input) {
40 (Some(lhs), Some(rhs)) => {
42 let input = lhs.parent();
43 let lhs = lhs.range();
44 let rhs = rhs.range();
45
46 match lhs.end == rhs.start {
47 true => Some(input.substr(lhs.start..rhs.end)),
48 false => None,
49 }
50 },
51 _ => None,
52 };
53
54 Self(ArenaIdx::new(Inner {
55 repr: AstStrRepr::Join { lhs: self, rhs: other },
56 input
57 }))
58 }
59
60 #[must_use]
62 pub fn repr(&self) -> &AstStrRepr {
63 &self.0.repr
64 }
65
66 #[must_use]
68 pub fn input(&self) -> Option<&Substr> {
69 self.0.input.as_ref()
70 }
71
72 #[must_use]
74 pub fn len(&self) -> usize {
75 self.repr().len()
76 }
77
78 #[must_use]
80 pub fn is_empty(&self) -> bool {
81 self.repr().is_empty()
82 }
83
84 #[must_use]
86 pub fn is_blank(&self) -> bool {
87 self.repr().is_blank()
88 }
89
90 #[must_use]
92 pub fn count_newlines(&self) -> usize {
93 self.repr().count_newlines()
94 }
95
96 #[must_use]
98 pub fn has_newlines(&self) -> bool {
99 self.repr().has_newlines()
100 }
101
102 #[must_use]
104 pub fn is_str(&self, other: &str) -> bool {
105 self.repr().is_str(other)
106 }
107
108 pub fn write(&self, output: &mut String) {
110 self.repr().write(output);
111 }
112
113 #[must_use]
115 pub fn str_cheap(&self) -> Option<&str> {
116 self.repr().str_cheap()
117 }
118
119 #[must_use]
121 pub fn str(&self) -> Cow<'_, str> {
122 self.repr().str()
123 }
124}
125
126impl serde::Serialize for AstStr {
128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: serde::Serializer
131 {
132 self.str().serialize(serializer)
133 }
134}
135
136impl<'de> serde::Deserialize<'de> for AstStr {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 let s = String::deserialize(deserializer)?;
142 Ok(Self::new(AstStrRepr::String(s.into())))
143 }
144}
145
146
147#[derive(PartialEq, Eq, Clone, Debug)]
148#[derive(ArenaData)]
149#[derive(derive_more::From)]
150struct Inner {
151 repr: AstStrRepr,
152 input: Option<Substr>,
153}
154
155#[derive(PartialEq, Eq, Clone, Debug)]
157#[derive(ArenaData)]
158#[derive(derive_more::From)]
159pub enum AstStrRepr {
160 #[from]
162 String(Substr),
163
164 #[from]
167 Static(&'static str),
168
169 #[from]
171 Char(char),
172
173 Spaces {
175 len: u16,
176 },
177
178 #[from]
180 Indentation {
181 indent: Arc<str>,
182 newlines: usize,
183 depth: usize,
184 },
185
186 Join {
188 lhs: AstStr,
189 rhs: AstStr,
190 },
191}
192
193impl AstStrRepr {
194 #[must_use]
196 pub fn len(&self) -> usize {
197 match *self {
198 Self::String(ref s) => s.len(),
199 Self::Static(s) => s.len(),
200 Self::Char(ch) => ch.len_utf8(),
201 Self::Spaces { len } => usize::from(len),
202 Self::Indentation { ref indent, newlines, depth, } => newlines + depth * indent.len(),
203 Self::Join { ref lhs, ref rhs } => lhs.len() + rhs.len(),
204 }
205 }
206
207 #[must_use]
209 pub fn is_empty(&self) -> bool {
210 self.len() == 0
211 }
212
213 #[must_use]
215 pub fn is_str_eq_to(&self, other: &Self) -> bool {
216 match (self, other) {
217 (lhs, rhs) if lhs == rhs => true,
219
220 (lhs, rhs) if let Some(lhs) = lhs.str_cheap() => rhs.is_str(lhs),
222 (lhs, rhs) if let Some(rhs) = rhs.str_cheap() => lhs.is_str(rhs),
223
224 _ => self.is_str(&other.str()),
228 }
229 }
230
231 #[must_use]
233 pub fn is_blank(&self) -> bool {
234 match *self {
235 Self::String(ref s) => crate::is_str_blank(s),
236 Self::Static(s) => crate::is_str_blank(s),
237 Self::Char(ch) => ch.is_ascii_whitespace(),
238 Self::Spaces { .. } | Self::Indentation { .. } => true,
239 Self::Join { ref lhs, ref rhs } => lhs.is_blank() && rhs.is_blank(),
240 }
241 }
242
243 #[must_use]
245 pub fn count_newlines(&self) -> usize {
246 match *self {
247 Self::String(ref s) => crate::str_count_newlines(s),
248 Self::Static(s) => crate::str_count_newlines(s),
249 Self::Char(ch) => match ch == '\n' {
250 true => 1,
251 false => 0,
252 },
253 Self::Spaces { .. } => 0,
254 Self::Indentation { newlines, .. } => newlines,
255 Self::Join { ref lhs, ref rhs } => lhs.count_newlines() + rhs.count_newlines(),
256 }
257 }
258
259 #[must_use]
261 pub fn has_newlines(&self) -> bool {
262 self.count_newlines() != 0
263 }
264
265 #[must_use]
267 pub fn is_str(&self, other: &str) -> bool {
268 match *self {
269 Self::String(ref s) => s == other,
270 Self::Static(s) => s == other,
271
272 Self::Char(ch) => {
273 let mut other = other.chars();
274 if other.next() != Some(ch) {
275 return false;
276 }
277
278 other.next().is_none()
279 },
280
281 Self::Spaces { len } => other.len() == usize::from(len) && other.chars().all(|ch| ch == ' '),
282
283 Self::Indentation { ref indent, newlines, depth } => other.len() == newlines + depth && other[..newlines].chars().all(|ch| ch == '\n') && other[newlines..]
284 .chunk(indent.len())
285 .all(|other_indent| other_indent == other),
286
287 Self::Join { ref lhs, ref rhs } => {
288 let Some((lhs_other, rhs_other)) = other.split_at_checked(lhs.len()) else {
289 return false;
290 };
291 lhs.is_str(lhs_other) && rhs.is_str(rhs_other)
292 },
293 }
294 }
295
296 pub fn write(&self, output: &mut String) {
298 match *self {
299 Self::String(ref s) => output.push_str(s),
300 Self::Static(s) => output.push_str(s),
301 Self::Char(ch) => output.push(ch),
302 Self::Spaces { len } => for _ in 0..len {
303 output.push(' ');
304 },
305 Self::Indentation { ref indent, newlines, depth, } => {
306 for _ in 0..newlines {
307 output.push('\n');
308 }
309 for _ in 0..depth {
310 output.push_str(indent);
311 }
312 },
313 Self::Join { ref lhs, ref rhs } => {
314 lhs.write(output);
315 rhs.write(output);
316 },
317 }
318 }
319
320 #[must_use]
322 pub fn str_cheap(&self) -> Option<&str> {
323 match self {
324 Self::String(s) => Some(s),
325
326 Self::Spaces { len: 0 } => "".into(),
328 Self::Spaces { len: 1 } => " ".into(),
329
330 _ => None,
331 }
332 }
333
334 #[must_use]
338 pub fn str(&self) -> Cow<'_, str> {
339 match self.str_cheap() {
340 Some(s) => s.into(),
341 None => {
342 let mut output = String::new();
343 self.write(&mut output);
344 output.into()
345 },
346 }
347 }
348}