1use std::{borrow::Cow, fmt, fmt::Write as _};
2
3use jslt_macro::expect_inner;
4use pest::iterators::Pairs;
5use serde_json::Value;
6
7use crate::{
8 context::{Context, builtins},
9 error::{JsltError, Result},
10 format,
11 parser::{FromPairs, Rule},
12 transform::{Transform, expr::ExprTransformer},
13};
14
15#[derive(Debug)]
16pub struct AccessorTransformer {
17 ident: String,
18 keys: Vec<KeyAccessorTransformer>,
19 nested: Option<Box<AccessorTransformer>>,
20}
21
22impl AccessorTransformer {
23 fn index_by_range<'i>(
24 input: &'i Value,
25 from: Option<&Value>,
26 to: Option<&Value>,
27 ) -> Result<Cow<'i, Value>> {
28 let Some(length) = builtins::length_impl(input) else {
29 return Ok(Cow::Owned(Value::Null));
30 };
31
32 let from = match from {
33 None | Some(Value::Null) => 0,
34 Some(index) if index.is_u64() => index.as_u64().expect("should be u64") as usize,
35 Some(back_index) if back_index.is_i64() => {
36 length - back_index.as_i64().expect("should be i64").unsigned_abs() as usize
37 }
38 Some(value) => return Err(JsltError::RangeNotNumber(value.clone())),
39 }
40 .min(length);
41
42 let to = match to {
43 None | Some(Value::Null) => length,
44 Some(index) if index.is_u64() => index.as_u64().expect("should be u64") as usize,
45 Some(back_index) if back_index.is_i64() => {
46 length - back_index.as_i64().expect("should be i64").unsigned_abs() as usize
47 }
48 Some(value) => return Err(JsltError::RangeNotNumber(value.clone())),
49 }
50 .min(length);
51
52 Ok(match input {
53 Value::Array(array) => Cow::Owned(array[from..to].into()),
54 Value::String(string) => Cow::Owned(string[from..to].into()),
55 _ => unreachable!(),
56 })
57 }
58
59 fn index_by_value<'i>(input: &'i Value, index: &Value) -> Result<Cow<'i, Value>> {
60 let Some(length) = builtins::length_impl(input) else {
61 return Ok(Cow::Owned(Value::Null));
62 };
63
64 match (input, index) {
65 (Value::String(str_input), Value::Number(num_key)) => num_key
66 .as_u64()
67 .map(|index| {
68 str_input
69 .chars()
70 .nth(index as usize)
71 .map(|val| Cow::Owned(Value::String(val.into())))
72 .unwrap_or_default()
73 })
74 .ok_or(JsltError::IndexOutOfRange),
75 (input, Value::String(str_key)) => Ok(Cow::Borrowed(&input[str_key])),
76 (input, Value::Number(num_key)) if num_key.is_u64() => {
77 let index = num_key.as_u64().expect("should be u64") as usize;
78 Ok(Cow::Borrowed(&input[index]))
79 }
80 (input, Value::Number(num_key)) if num_key.is_i64() => {
81 let index =
82 (length - num_key.as_i64().expect("should be i64").unsigned_abs() as usize).min(length);
83
84 Ok(Cow::Borrowed(&input[index]))
85 }
86 _ => Err(JsltError::IndexOutOfRange),
87 }
88 }
89}
90
91impl FromPairs for AccessorTransformer {
92 fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
93 let pairs = expect_inner!(pairs, Rule::Accessor)?;
94
95 let mut ident = None;
96 let mut nested = None;
97 let mut keys = Vec::new();
98
99 for pair in pairs {
100 match pair.as_rule() {
101 Rule::Ident => ident = Some(pair.as_str()),
102 Rule::String => ident = Some(pair.into_inner().as_str()),
103 Rule::KeyAccessor => keys.push(KeyAccessorTransformer::from_pairs(&mut pair.into_inner())?),
104 Rule::Accessor => {
105 nested = Some(Box::new(AccessorTransformer::from_pairs(
106 &mut Pairs::single(pair),
107 )?));
108 }
109 _ => break,
110 }
111 }
112
113 let ident = ident.unwrap_or("").to_owned();
114
115 Ok(AccessorTransformer {
116 ident,
117 keys,
118 nested,
119 })
120 }
121}
122
123impl Transform for AccessorTransformer {
124 #[allow(clippy::only_used_in_recursion)]
125 fn transform_value(&self, context: Context<'_>, input: &Value) -> Result<Value> {
126 let mut value = (!self.ident.is_empty())
127 .then(|| &input[&self.ident])
128 .unwrap_or(input);
129
130 let mut temp_value_store = None;
131
132 for key in &self.keys {
133 let next_value = match key {
134 KeyAccessorTransformer::Index(index) => {
135 let result = AccessorTransformer::index_by_value(
136 value,
137 &index.transform_value(context.clone(), input)?,
138 )?;
139
140 match result {
141 Cow::Borrowed(value) => value,
142 Cow::Owned(owned) => {
143 temp_value_store.replace(owned);
144 temp_value_store.as_ref().unwrap()
145 }
146 }
147 }
148 KeyAccessorTransformer::Range { from, to } => {
149 let from = from
150 .as_ref()
151 .map(|value| value.transform_value(context.clone(), input))
152 .transpose()?;
153
154 let to = to
155 .as_ref()
156 .map(|value| value.transform_value(context.clone(), input))
157 .transpose()?;
158
159 let result = AccessorTransformer::index_by_range(value, from.as_ref(), to.as_ref())?;
160
161 match result {
162 Cow::Borrowed(value) => value,
163 Cow::Owned(owned) => {
164 temp_value_store.replace(owned);
165 temp_value_store.as_ref().unwrap()
166 }
167 }
168 }
169 };
170
171 value = next_value;
172 }
173
174 match &self.nested {
175 Some(nested) => nested.transform_value(context, value),
176 None => Ok(value.clone()),
177 }
178 }
179}
180
181impl format::Display for AccessorTransformer {
182 fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
183 let AccessorTransformer {
184 ident,
185 keys,
186 nested,
187 } = self;
188
189 if !ident.is_empty() {
190 write!(f, ".{ident}")?;
191 }
192
193 for key in keys {
194 format::Display::fmt(key, f)?;
195 }
196
197 if let Some(nested) = nested {
198 format::Display::fmt(nested.as_ref(), f)?;
199 }
200
201 Ok(())
202 }
203}
204
205pub struct RootAccessorDisplay<'a>(pub &'a AccessorTransformer);
206
207impl format::Display for RootAccessorDisplay<'_> {
208 fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
209 let RootAccessorDisplay(accessor) = *self;
210
211 if accessor.ident.is_empty() {
212 f.write_char('.')?;
213 }
214
215 format::Display::fmt(accessor, f)
216 }
217}
218
219#[derive(Debug)]
220pub enum KeyAccessorTransformer {
221 Index(ExprTransformer),
222 Range {
223 from: Option<ExprTransformer>,
224 to: Option<ExprTransformer>,
225 },
226}
227
228impl FromPairs for KeyAccessorTransformer {
229 fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
230 let inner = pairs.peek().ok_or(JsltError::UnexpectedEnd)?;
231
232 match inner.as_rule() {
233 Rule::RangeAccessor => {
234 let mut inner = pairs.next().expect("Sould be fine").into_inner();
235
236 let kind = inner.next().ok_or(JsltError::UnexpectedEnd)?;
237
238 match kind.as_rule() {
239 Rule::FromRangeAccessor => {
240 let mut inner = kind.into_inner();
241
242 let from = ExprTransformer::from_pairs(
243 &mut inner
244 .next()
245 .ok_or(JsltError::UnexpectedContent(Rule::RangeAccessor))
246 .map(Pairs::single)?,
247 )?;
248
249 let to = inner
250 .next()
251 .map(|pair| ExprTransformer::from_pairs(&mut Pairs::single(pair)))
252 .transpose()?;
253
254 Ok(KeyAccessorTransformer::Range {
255 from: Some(from),
256 to,
257 })
258 }
259 Rule::ToRangeAccessor => {
260 let to = ExprTransformer::from_pairs(&mut kind.into_inner())?;
261
262 Ok(KeyAccessorTransformer::Range {
263 from: None,
264 to: Some(to),
265 })
266 }
267 _ => Err(JsltError::UnexpectedContent(Rule::RangeAccessor)),
268 }
269 }
270 _ => Ok(KeyAccessorTransformer::Index(ExprTransformer::from_pairs(
271 pairs,
272 )?)),
273 }
274 }
275}
276
277impl format::Display for KeyAccessorTransformer {
278 fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
279 f.write_char('[')?;
280
281 match self {
282 KeyAccessorTransformer::Index(expr) => format::Display::fmt(expr, f)?,
283 KeyAccessorTransformer::Range { from, to } => {
284 if let Some(from) = from {
285 format::Display::fmt(from, f)?;
286 }
287
288 f.write_char(':')?;
289
290 if let Some(to) = to {
291 format::Display::fmt(to, f)?;
292 }
293 }
294 }
295
296 f.write_char(']')?;
297 Ok(())
298 }
299}