1use crate::error::{Error, ErrorKind};
2use crate::value::{DynObject, ObjectRepr, Value, ValueKind, ValueRepr};
3
4const MIN_I128_AS_POS_U128: u128 = 170141183460469231731687303715884105728;
5
6pub enum CoerceResult<'a> {
7 I128(i128, i128),
8 F64(f64, f64),
9 Str(&'a str, &'a str),
10}
11
12pub(crate) fn as_f64(value: &Value, lossy: bool) -> Option<f64> {
13 macro_rules! checked {
14 ($expr:expr, $ty:ty) => {{
15 let rv = $expr as f64;
16 return if lossy || rv as $ty == $expr {
17 Some(rv)
18 } else {
19 None
20 };
21 }};
22 }
23
24 Some(match value.0 {
25 ValueRepr::Bool(x) => x as i64 as f64,
26 ValueRepr::U64(x) => checked!(x, u64),
27 ValueRepr::U128(x) => checked!(x.0, u128),
28 ValueRepr::I64(x) => checked!(x, i64),
29 ValueRepr::I128(x) => checked!(x.0, i128),
30 ValueRepr::F64(x) => x,
31 _ => return None,
32 })
33}
34
35pub fn coerce<'x>(a: &'x Value, b: &'x Value, lossy: bool) -> Option<CoerceResult<'x>> {
36 match (&a.0, &b.0) {
37 (ValueRepr::U64(a), ValueRepr::U64(b)) => Some(CoerceResult::I128(*a as i128, *b as i128)),
39 (ValueRepr::U128(a), ValueRepr::U128(b)) => {
40 Some(CoerceResult::I128(a.0 as i128, b.0 as i128))
41 }
42 (ValueRepr::String(a, _), ValueRepr::String(b, _)) => Some(CoerceResult::Str(a, b)),
43 (ValueRepr::SmallStr(a), ValueRepr::SmallStr(b)) => {
44 Some(CoerceResult::Str(a.as_str(), b.as_str()))
45 }
46 (ValueRepr::SmallStr(a), ValueRepr::String(b, _)) => Some(CoerceResult::Str(a.as_str(), b)),
47 (ValueRepr::String(a, _), ValueRepr::SmallStr(b)) => Some(CoerceResult::Str(a, b.as_str())),
48 (ValueRepr::I64(a), ValueRepr::I64(b)) => Some(CoerceResult::I128(*a as i128, *b as i128)),
49 (ValueRepr::I128(a), ValueRepr::I128(b)) => Some(CoerceResult::I128(a.0, b.0)),
50 (ValueRepr::F64(a), ValueRepr::F64(b)) => Some(CoerceResult::F64(*a, *b)),
51
52 (ValueRepr::F64(a), _) => Some(CoerceResult::F64(*a, some!(as_f64(b, lossy)))),
54 (_, ValueRepr::F64(b)) => Some(CoerceResult::F64(some!(as_f64(a, lossy)), *b)),
55
56 _ => Some(CoerceResult::I128(
58 some!(i128::try_from(a.clone()).ok()),
59 some!(i128::try_from(b.clone()).ok()),
60 )),
61 }
62}
63
64fn get_offset_and_len<F: FnOnce() -> usize>(
65 start: Option<i64>,
66 stop: Option<i64>,
67 end: F,
68) -> (usize, usize) {
69 let start = start.unwrap_or(0);
70 if start < 0 || stop.map_or(true, |x| x < 0) {
71 let end = end();
72 let start = if start < 0 {
73 (end as i64 + start) as usize
74 } else {
75 start as usize
76 };
77 let stop = match stop {
78 None => end,
79 Some(x) if x < 0 => (end as i64 + x) as usize,
80 Some(x) => x as usize,
81 };
82 (start, stop.saturating_sub(start))
83 } else {
84 (
85 start as usize,
86 (stop.unwrap() as usize).saturating_sub(start as usize),
87 )
88 }
89}
90
91fn range_step_backwards(
92 start: Option<i64>,
93 stop: Option<i64>,
94 step: usize,
95 end: usize,
96) -> impl Iterator<Item = usize> {
97 let start = match start {
98 None => end.saturating_sub(1),
99 Some(start) if start >= end as i64 => end.saturating_sub(1),
100 Some(start) if start >= 0 => start as usize,
101 Some(start) => (end as i64 + start).max(0) as usize,
102 };
103 let stop = match stop {
104 None => 0,
105 Some(stop) if stop < 0 => (end as i64 + stop).max(0) as usize,
106 Some(stop) => stop as usize,
107 };
108 let length = if stop == 0 {
109 (start + step) / step
110 } else {
111 (start - stop + step - 1) / step
112 };
113 (stop..=start).rev().step_by(step).take(length)
114}
115
116pub fn slice(value: Value, start: Value, stop: Value, step: Value) -> Result<Value, Error> {
117 let start = if start.is_none() {
118 None
119 } else {
120 Some(ok!(start.try_into()))
121 };
122 let stop = if stop.is_none() {
123 None
124 } else {
125 Some(ok!(i64::try_from(stop)))
126 };
127 let step = if step.is_none() {
128 1i64
129 } else {
130 ok!(i64::try_from(step))
131 };
132 if step == 0 {
133 return Err(Error::new(
134 ErrorKind::InvalidOperation,
135 "cannot slice by step size of 0",
136 ));
137 }
138
139 let kind = value.kind();
140 let error = Err(Error::new(
141 ErrorKind::InvalidOperation,
142 format!("value of type {} cannot be sliced", kind),
143 ));
144
145 match value.0 {
146 ValueRepr::String(..) | ValueRepr::SmallStr(_) => {
147 let s = value.as_str().unwrap();
148 if step > 0 {
149 let (start, len) = get_offset_and_len(start, stop, || s.chars().count());
150 Ok(Value::from(
151 s.chars()
152 .skip(start)
153 .take(len)
154 .step_by(step as usize)
155 .collect::<String>(),
156 ))
157 } else {
158 let chars: Vec<char> = s.chars().collect();
159 Ok(Value::from(
160 range_step_backwards(start, stop, -step as usize, chars.len())
161 .map(move |i| chars[i])
162 .collect::<String>(),
163 ))
164 }
165 }
166 ValueRepr::Bytes(ref b) => {
167 if step > 0 {
168 let (start, len) = get_offset_and_len(start, stop, || b.len());
169 Ok(Value::from_bytes(
170 b.iter()
171 .skip(start)
172 .take(len)
173 .step_by(step as usize)
174 .copied()
175 .collect(),
176 ))
177 } else {
178 Ok(Value::from_bytes(
179 range_step_backwards(start, stop, -step as usize, b.len())
180 .map(|i| b[i])
181 .collect::<Vec<u8>>(),
182 ))
183 }
184 }
185 ValueRepr::Undefined(_) | ValueRepr::None => Ok(Value::from(Vec::<Value>::new())),
186 ValueRepr::Object(obj) if matches!(obj.repr(), ObjectRepr::Seq | ObjectRepr::Iterable) => {
187 if step > 0 {
188 let len = obj.enumerator_len().unwrap_or_default();
189 let (start, len) = get_offset_and_len(start, stop, || len);
190 Ok(Value::make_object_iterable(obj, move |obj| {
191 if let Some(iter) = obj.try_iter() {
192 Box::new(iter.skip(start).take(len).step_by(step as usize))
193 } else {
194 Box::new(None.into_iter())
195 }
196 }))
197 } else {
198 Ok(Value::make_object_iterable(obj.clone(), move |obj| {
199 if let Some(iter) = obj.try_iter() {
200 let vec: Vec<Value> = iter.collect();
201 Box::new(
202 range_step_backwards(start, stop, -step as usize, vec.len())
203 .map(move |i| vec[i].clone()),
204 )
205 } else {
206 Box::new(None.into_iter())
207 }
208 }))
209 }
210 }
211 _ => error,
212 }
213}
214
215fn int_as_value(val: i128) -> Value {
216 if val as i64 as i128 == val {
217 (val as i64).into()
218 } else {
219 val.into()
220 }
221}
222
223fn impossible_op(op: &str, lhs: &Value, rhs: &Value) -> Error {
224 Error::new(
225 ErrorKind::InvalidOperation,
226 format!(
227 "tried to use {} operator on unsupported types {} and {}",
228 op,
229 lhs.kind(),
230 rhs.kind()
231 ),
232 )
233}
234
235fn failed_op(op: &str, lhs: &Value, rhs: &Value) -> Error {
236 Error::new(
237 ErrorKind::InvalidOperation,
238 format!("unable to calculate {lhs} {op} {rhs}"),
239 )
240}
241
242macro_rules! math_binop {
243 ($name:ident, $int:ident, $float:tt) => {
244 pub fn $name(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
245 match coerce(lhs, rhs, true) {
246 Some(CoerceResult::I128(a, b)) => match a.$int(b) {
247 Some(val) => Ok(int_as_value(val)),
248 None => Err(failed_op(stringify!($float), lhs, rhs))
249 },
250 Some(CoerceResult::F64(a, b)) => Ok((a $float b).into()),
251 _ => Err(impossible_op(stringify!($float), lhs, rhs))
252 }
253 }
254 }
255}
256
257pub fn add(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
258 if matches!(lhs.kind(), ValueKind::Seq | ValueKind::Iterable)
259 && matches!(rhs.kind(), ValueKind::Seq | ValueKind::Iterable)
260 {
261 let lhs = lhs.clone();
262 let rhs = rhs.clone();
263 return Ok(Value::make_iterable(move || {
264 if let Ok(lhs) = lhs.try_iter() {
265 if let Ok(rhs) = rhs.try_iter() {
266 return Box::new(lhs.chain(rhs))
267 as Box<dyn Iterator<Item = Value> + Send + Sync>;
268 }
269 }
270 Box::new(None.into_iter()) as Box<dyn Iterator<Item = Value> + Send + Sync>
271 }));
272 }
273 match coerce(lhs, rhs, true) {
274 Some(CoerceResult::I128(a, b)) => a
275 .checked_add(b)
276 .ok_or_else(|| failed_op("+", lhs, rhs))
277 .map(int_as_value),
278 Some(CoerceResult::F64(a, b)) => Ok((a + b).into()),
279 Some(CoerceResult::Str(a, b)) => Ok(Value::from([a, b].concat())),
280 _ => Err(impossible_op("+", lhs, rhs)),
281 }
282}
283
284math_binop!(sub, checked_sub, -);
285math_binop!(rem, checked_rem_euclid, %);
286
287pub fn mul(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
288 if let Some((s, n)) = lhs
289 .as_str()
290 .map(|s| (s, rhs))
291 .or_else(|| rhs.as_str().map(|s| (s, lhs)))
292 {
293 return Ok(Value::from(s.repeat(ok!(n.as_usize().ok_or_else(|| {
294 Error::new(
295 ErrorKind::InvalidOperation,
296 "strings can only be multiplied with integers",
297 )
298 })))));
299 } else if let Some((seq, n)) = lhs
300 .as_object()
301 .map(|s| (s, rhs))
302 .or_else(|| rhs.as_object().map(|s| (s, lhs)))
303 .filter(|x| matches!(x.0.repr(), ObjectRepr::Iterable | ObjectRepr::Seq))
304 {
305 return repeat_iterable(n, seq);
306 }
307
308 match coerce(lhs, rhs, true) {
309 Some(CoerceResult::I128(a, b)) => match a.checked_mul(b) {
310 Some(val) => Ok(int_as_value(val)),
311 None => Err(failed_op(stringify!(*), lhs, rhs)),
312 },
313 Some(CoerceResult::F64(a, b)) => Ok((a * b).into()),
314 _ => Err(impossible_op(stringify!(*), lhs, rhs)),
315 }
316}
317
318fn repeat_iterable(n: &Value, seq: &DynObject) -> Result<Value, Error> {
319 struct LenIterWrap<I: Send + Sync>(usize, I);
320
321 impl<I: Iterator<Item = Value> + Send + Sync> Iterator for LenIterWrap<I> {
322 type Item = Value;
323
324 #[inline(always)]
325 fn next(&mut self) -> Option<Self::Item> {
326 self.1.next()
327 }
328
329 #[inline(always)]
330 fn size_hint(&self) -> (usize, Option<usize>) {
331 (self.0, Some(self.0))
332 }
333 }
334
335 let n = ok!(n.as_usize().ok_or_else(|| {
336 Error::new(
337 ErrorKind::InvalidOperation,
338 "sequences and iterables can only be multiplied with integers",
339 )
340 }));
341
342 let len = ok!(seq.enumerator_len().ok_or_else(|| {
343 Error::new(
344 ErrorKind::InvalidOperation,
345 "cannot repeat unsized iterables",
346 )
347 }));
348
349 Ok(Value::make_object_iterable(seq.clone(), move |seq| {
355 Box::new(LenIterWrap(
356 len * n,
357 (0..n).flat_map(move |_| {
358 seq.try_iter().unwrap_or_else(|| {
359 Box::new(
360 std::iter::repeat(Value::from(Error::new(
361 ErrorKind::InvalidOperation,
362 "iterable did not iterate against expectations",
363 )))
364 .take(len),
365 )
366 })
367 }),
368 ))
369 }))
370}
371
372pub fn div(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
373 fn do_it(lhs: &Value, rhs: &Value) -> Option<Value> {
374 let a = some!(as_f64(lhs, true));
375 let b = some!(as_f64(rhs, true));
376 Some((a / b).into())
377 }
378 do_it(lhs, rhs).ok_or_else(|| impossible_op("/", lhs, rhs))
379}
380
381pub fn int_div(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
382 match coerce(lhs, rhs, true) {
383 Some(CoerceResult::I128(a, b)) => {
384 if b != 0 {
385 a.checked_div_euclid(b)
386 .ok_or_else(|| failed_op("//", lhs, rhs))
387 .map(int_as_value)
388 } else {
389 Err(failed_op("//", lhs, rhs))
390 }
391 }
392 Some(CoerceResult::F64(a, b)) => Ok(a.div_euclid(b).into()),
393 _ => Err(impossible_op("//", lhs, rhs)),
394 }
395}
396
397pub fn pow(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
399 match coerce(lhs, rhs, true) {
400 Some(CoerceResult::I128(a, b)) => {
401 match TryFrom::try_from(b).ok().and_then(|b| a.checked_pow(b)) {
402 Some(val) => Ok(int_as_value(val)),
403 None => Err(failed_op("**", lhs, rhs)),
404 }
405 }
406 Some(CoerceResult::F64(a, b)) => Ok((a.powf(b)).into()),
407 _ => Err(impossible_op("**", lhs, rhs)),
408 }
409}
410
411pub fn neg(val: &Value) -> Result<Value, Error> {
413 if val.kind() == ValueKind::Number {
414 match val.0 {
415 ValueRepr::F64(x) => Ok((-x).into()),
416 ValueRepr::U128(x) if x.0 == MIN_I128_AS_POS_U128 => {
419 Ok(Value::from(MIN_I128_AS_POS_U128))
420 }
421 _ => {
422 if let Ok(x) = i128::try_from(val.clone()) {
423 x.checked_mul(-1)
424 .ok_or_else(|| Error::new(ErrorKind::InvalidOperation, "overflow"))
425 .map(int_as_value)
426 } else {
427 Err(Error::from(ErrorKind::InvalidOperation))
428 }
429 }
430 }
431 } else {
432 Err(Error::from(ErrorKind::InvalidOperation))
433 }
434}
435
436pub fn string_concat(left: Value, right: &Value) -> Value {
438 Value::from(format!("{left}{right}"))
439}
440
441pub fn contains(container: &Value, value: &Value) -> Result<Value, Error> {
443 if container.is_undefined() {
446 return Ok(Value::from(false));
447 }
448 let rv = if let Some(s) = container.as_str() {
449 if let Some(s2) = value.as_str() {
450 s.contains(s2)
451 } else {
452 s.contains(&value.to_string())
453 }
454 } else if let ValueRepr::Object(ref obj) = container.0 {
455 match obj.repr() {
456 ObjectRepr::Plain => false,
457 ObjectRepr::Map => obj.get_value(value).is_some(),
458 ObjectRepr::Seq | ObjectRepr::Iterable => {
459 obj.try_iter().into_iter().flatten().any(|v| &v == value)
460 }
461 }
462 } else {
463 return Err(Error::new(
464 ErrorKind::InvalidOperation,
465 "cannot perform a containment check on this value",
466 ));
467 };
468 Ok(Value::from(rv))
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 use similar_asserts::assert_eq;
476
477 #[test]
478 fn test_neg() {
479 let err = neg(&Value::from(i128::MIN)).unwrap_err();
480 assert_eq!(err.to_string(), "invalid operation: overflow");
481 }
482
483 #[test]
484 fn test_adding() {
485 let err = add(&Value::from("a"), &Value::from(42)).unwrap_err();
486 assert_eq!(
487 err.to_string(),
488 "invalid operation: tried to use + operator on unsupported types string and number"
489 );
490
491 assert_eq!(
492 add(&Value::from(1), &Value::from(2)).unwrap(),
493 Value::from(3)
494 );
495 assert_eq!(
496 add(&Value::from("foo"), &Value::from("bar")).unwrap(),
497 Value::from("foobar")
498 );
499
500 let err = add(&Value::from(i128::MAX), &Value::from(1)).unwrap_err();
501 assert_eq!(
502 err.to_string(),
503 "invalid operation: unable to calculate 170141183460469231731687303715884105727 + 1"
504 );
505 }
506
507 #[test]
508 fn test_subtracting() {
509 let err = sub(&Value::from("a"), &Value::from(42)).unwrap_err();
510 assert_eq!(
511 err.to_string(),
512 "invalid operation: tried to use - operator on unsupported types string and number"
513 );
514
515 let err = sub(&Value::from("foo"), &Value::from("bar")).unwrap_err();
516 assert_eq!(
517 err.to_string(),
518 "invalid operation: tried to use - operator on unsupported types string and string"
519 );
520
521 assert_eq!(
522 sub(&Value::from(2), &Value::from(1)).unwrap(),
523 Value::from(1)
524 );
525 }
526
527 #[test]
528 fn test_dividing() {
529 let err = div(&Value::from("a"), &Value::from(42)).unwrap_err();
530 assert_eq!(
531 err.to_string(),
532 "invalid operation: tried to use / operator on unsupported types string and number"
533 );
534
535 let err = div(&Value::from("foo"), &Value::from("bar")).unwrap_err();
536 assert_eq!(
537 err.to_string(),
538 "invalid operation: tried to use / operator on unsupported types string and string"
539 );
540
541 assert_eq!(
542 div(&Value::from(100), &Value::from(2)).unwrap(),
543 Value::from(50.0)
544 );
545
546 let err = int_div(&Value::from(i128::MIN), &Value::from(-1i128)).unwrap_err();
547 assert_eq!(
548 err.to_string(),
549 "invalid operation: unable to calculate -170141183460469231731687303715884105728 // -1"
550 );
551 }
552
553 #[test]
554 fn test_concat() {
555 assert_eq!(
556 string_concat(Value::from("foo"), &Value::from(42)),
557 Value::from("foo42")
558 );
559 assert_eq!(
560 string_concat(Value::from(23), &Value::from(42)),
561 Value::from("2342")
562 );
563 }
564
565 #[test]
566 fn test_slicing() {
567 let v = Value::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
568
569 assert_eq!(
571 slice(v.clone(), Value::from(()), Value::from(()), Value::from(())).unwrap(),
572 Value::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
573 );
574
575 assert_eq!(
577 slice(v.clone(), Value::from(()), Value::from(()), Value::from(2)).unwrap(),
578 Value::from(vec![0, 2, 4, 6, 8])
579 );
580
581 assert_eq!(
583 slice(v.clone(), Value::from(1), Value::from(2), Value::from(2)).unwrap(),
584 Value::from(vec![1])
585 );
586
587 assert_eq!(
589 slice(v.clone(), Value::from(()), Value::from(()), Value::from(-2)).unwrap(),
590 Value::from(vec![9, 7, 5, 3, 1])
591 );
592
593 assert_eq!(
595 slice(v.clone(), Value::from(2), Value::from(()), Value::from(-2)).unwrap(),
596 Value::from(vec![2, 0])
597 );
598
599 assert_eq!(
601 slice(v.clone(), Value::from(4), Value::from(2), Value::from(-2)).unwrap(),
602 Value::from(vec![4])
603 );
604
605 assert_eq!(
607 slice(v.clone(), Value::from(8), Value::from(3), Value::from(-2)).unwrap(),
608 Value::from(vec![8, 6, 4])
609 );
610 }
611
612 #[test]
613 fn test_string_slicing() {
614 let s = Value::from("abcdefghij");
615
616 assert_eq!(
618 slice(s.clone(), Value::from(()), Value::from(()), Value::from(())).unwrap(),
619 Value::from("abcdefghij")
620 );
621
622 assert_eq!(
624 slice(s.clone(), Value::from(()), Value::from(()), Value::from(2)).unwrap(),
625 Value::from("acegi")
626 );
627
628 assert_eq!(
630 slice(s.clone(), Value::from(1), Value::from(2), Value::from(2)).unwrap(),
631 Value::from("b")
632 );
633
634 assert_eq!(
636 slice(s.clone(), Value::from(()), Value::from(()), Value::from(-2)).unwrap(),
637 Value::from("jhfdb")
638 );
639
640 assert_eq!(
642 slice(s.clone(), Value::from(2), Value::from(()), Value::from(-2)).unwrap(),
643 Value::from("ca")
644 );
645
646 assert_eq!(
648 slice(s.clone(), Value::from(4), Value::from(2), Value::from(-2)).unwrap(),
649 Value::from("e")
650 );
651
652 assert_eq!(
654 slice(s.clone(), Value::from(8), Value::from(3), Value::from(-2)).unwrap(),
655 Value::from("ige")
656 );
657 }
658
659 #[test]
660 fn test_bytes_slicing() {
661 let s = Value::from_bytes(b"abcdefghij".to_vec());
662
663 assert_eq!(
665 slice(s.clone(), Value::from(()), Value::from(()), Value::from(())).unwrap(),
666 Value::from_bytes(b"abcdefghij".to_vec())
667 );
668
669 assert_eq!(
671 slice(s.clone(), Value::from(()), Value::from(()), Value::from(2)).unwrap(),
672 Value::from_bytes(b"acegi".to_vec())
673 );
674
675 assert_eq!(
677 slice(s.clone(), Value::from(1), Value::from(2), Value::from(2)).unwrap(),
678 Value::from_bytes(b"b".to_vec())
679 );
680
681 assert_eq!(
683 slice(s.clone(), Value::from(()), Value::from(()), Value::from(-2)).unwrap(),
684 Value::from_bytes(b"jhfdb".to_vec())
685 );
686
687 assert_eq!(
689 slice(s.clone(), Value::from(2), Value::from(()), Value::from(-2)).unwrap(),
690 Value::from_bytes(b"ca".to_vec())
691 );
692
693 assert_eq!(
695 slice(s.clone(), Value::from(4), Value::from(2), Value::from(-2)).unwrap(),
696 Value::from_bytes(b"e".to_vec())
697 );
698
699 assert_eq!(
701 slice(s.clone(), Value::from(8), Value::from(3), Value::from(-2)).unwrap(),
702 Value::from_bytes(b"ige".to_vec())
703 );
704 }
705}