jslt/transform/value/
accessor.rs

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}