tytanic_filter/ast/
str.rs1use std::fmt::Debug;
2use std::ops::Deref;
3
4use ecow::{eco_vec, EcoString};
5use pest::iterators::Pair;
6
7use super::{Error, PairExt, PairsExt, Rule};
8use crate::eval::{self, Context, Eval, Test, TryFromValue, Type, Value};
9
10#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct Str(pub EcoString);
13
14impl Str {
15 pub fn as_str(&self) -> &str {
17 self.0.as_str()
18 }
19
20 pub fn into_inner(self) -> EcoString {
22 self.0
23 }
24}
25
26impl Debug for Str {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 self.0.fmt(f)
29 }
30}
31
32impl Deref for Str {
33 type Target = str;
34
35 fn deref(&self) -> &Self::Target {
36 self.as_str()
37 }
38}
39
40impl AsRef<str> for Str {
41 fn as_ref(&self) -> &str {
42 self.as_str()
43 }
44}
45
46impl From<EcoString> for Str {
47 fn from(value: EcoString) -> Self {
48 Self(value)
49 }
50}
51
52impl From<String> for Str {
53 fn from(value: String) -> Self {
54 Self(value.into())
55 }
56}
57
58impl From<&str> for Str {
59 fn from(value: &str) -> Self {
60 Self(value.into())
61 }
62}
63
64impl From<Str> for EcoString {
65 fn from(value: Str) -> Self {
66 value.into_inner()
67 }
68}
69
70impl<T: Test> Eval<T> for Str {
71 fn eval(&self, _ctx: &Context<T>) -> Result<Value<T>, eval::Error> {
72 Ok(Value::Str(self.clone()))
73 }
74}
75
76impl<T> TryFromValue<T> for Str {
77 fn try_from_value(value: Value<T>) -> Result<Self, eval::Error> {
78 Ok(match value {
79 Value::Str(str) => str,
80 _ => {
81 return Err(eval::Error::TypeMismatch {
82 expected: eco_vec![Type::Str],
83 found: value.as_type(),
84 })
85 }
86 })
87 }
88}
89
90impl Str {
91 pub(super) fn parse(pair: Pair<'_, Rule>) -> Result<Self, Error> {
92 pair.expect_rules(&[Rule::str_single, Rule::str_double])?;
93
94 let mut pairs = pair.into_inner();
95 let start = pairs.expect_pair(&[Rule::str_single_delim, Rule::str_double_delim])?;
96 let inner = pairs.expect_pair(&[Rule::str_single_inner, Rule::str_double_inner])?;
97 let _ = pairs.expect_pair(&[start.as_rule()])?;
98 pairs.expect_end()?;
99
100 match inner.as_rule() {
101 Rule::str_single_inner => Ok(Self(inner.as_str().into())),
102 Rule::str_double_inner => {
103 if !inner.as_str().contains('\\') {
104 Ok(Self(inner.as_str().into()))
105 } else {
106 let mut buf = String::with_capacity(inner.as_str().len());
107
108 let mut rest = inner.as_str();
109 while let Some((lit, esc)) = rest.split_once('\\') {
110 buf.push_str(lit);
111
112 if esc.starts_with(['\\', '"', 'n', 'r', 't']) {
113 match esc.as_bytes()[0] {
114 b'\\' => buf.push('\\'),
115 b'"' => buf.push('"'),
116 b'n' => buf.push('\n'),
117 b'r' => buf.push('\r'),
118 b't' => buf.push('\t'),
119 _ => unreachable!(),
120 }
121 rest = &esc[1..];
122 } else if let Some(esc) = esc.strip_prefix("u{") {
123 let (digits, other) =
124 esc.split_once('}').expect("parser ensure closing '}'");
125
126 buf.push(
127 u32::from_str_radix(digits, 16)
128 .expect("parser ensures hex digits only")
129 .try_into()?,
130 );
131
132 rest = other;
133 } else {
134 unreachable!(
135 "unhandled string escape sequence: {:?}",
136 esc.split_once(' ').map(|(p, _)| p).unwrap_or(esc)
137 );
138 }
139 }
140
141 Ok(Self(buf.into()))
142 }
143 }
144 _ => unreachable!(),
145 }
146 }
147}