tytanic_filter/ast/
str.rs

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