mod common;
use common::*;
use simple_update_in::{Accessor, Selector, Value};
fn num(v: &Value) -> f64 {
match v {
Value::Number(x) => *x,
_ => 0.0,
}
}
#[test]
fn update_array_using_predicate() {
let from = arr(vec![n(1.0), n(2.0), n(3.0), n(4.0), n(5.0)]);
let pred = Accessor::predicate(|v, sel| {
let even_value = num(v) % 2.0 == 0.0;
let odd_index = matches!(sel, Selector::Index(i) if i % 2 == 1);
even_value && odd_index
});
let actual = run(from, &[pred], multiply(10.0));
assert_eq!(actual, arr(vec![n(1.0), n(20.0), n(3.0), n(40.0), n(5.0)]),);
}
#[test]
fn update_map_using_predicate() {
let from = obj(vec![
("abc", n(123.0)),
("def", n(456.0)),
("ghi", n(789.0)),
]);
let pred = Accessor::predicate(|v, sel| {
let odd = (num(v) as i64) % 2 != 0;
let key_ok = matches!(sel, Selector::Key(k) if *k == "abc" || *k == "ghi");
odd && key_ok
});
let actual = run(from, &[pred], multiply(10.0));
assert_eq!(
actual,
obj(vec![
("abc", n(1230.0)),
("def", n(456.0)),
("ghi", n(7890.0)),
]),
);
}
#[test]
fn update_using_multiple_predicates() {
let from = obj(vec![
("one", arr(vec![n(1.1), n(1.2), n(1.3)])),
("two", arr(vec![n(2.1), n(2.2), n(2.3)])),
]);
let always = Accessor::predicate(|_, _| true);
let pick = Accessor::predicate(|v, _| num(v) == 1.2 || num(v) == 2.2);
let actual = run(from, &[always, pick], multiply(10.0));
assert_eq!(
actual,
obj(vec![
("one", arr(vec![n(1.1), n(12.0), n(1.3)])),
("two", arr(vec![n(2.1), n(22.0), n(2.3)])),
]),
);
}
#[test]
fn predicate_removal_splices_left_to_right() {
let from = arr(vec![n(10.0), n(20.0)]);
let always = Accessor::predicate(|_, _| true);
let actual = remove(from, &[always]);
assert_eq!(actual, arr(vec![n(20.0)]));
}
#[test]
fn predicate_without_match_in_array_is_no_op() {
let from = arr(vec![n(1.0), n(2.0), n(3.0), n(4.0), n(5.0)]);
let pred = Accessor::predicate(|_, _| false);
let actual = run(from.clone(), &[pred], constant(n(0.0)));
assert!(from.ptr_eq(&actual));
}
#[test]
fn predicate_without_match_in_map_is_no_op() {
let from = obj(vec![
("abc", n(123.0)),
("def", n(456.0)),
("ghi", n(789.0)),
]);
let pred = Accessor::predicate(|_, _| false);
let actual = run(from.clone(), &[pred], constant(n(0.0)));
assert!(from.ptr_eq(&actual));
}
#[test]
fn array_auto_creation_using_predicate() {
let from = arr(vec![arr(vec![n(1.1)]), arr(vec![n(2.2)])]);
let always = Accessor::predicate(|_, _| true);
let actual = run(from, &[always, idx(1)], constant(s("fin")));
assert_eq!(
actual,
arr(vec![
arr(vec![n(1.1), s("fin")]),
arr(vec![n(2.2), s("fin")]),
]),
);
}
#[test]
fn map_auto_creation_using_predicate() {
let from = obj(vec![("abc", obj(vec![])), ("def", obj(vec![]))]);
let always = Accessor::predicate(|_, _| true);
let actual = run(from, &[always, key("value")], constant(n(123.0)));
assert_eq!(
actual,
obj(vec![
("abc", obj(vec![("value", n(123.0))])),
("def", obj(vec![("value", n(123.0))])),
]),
);
}
#[test]
fn array_partial_update_using_predicate_shares_others() {
let from = arr(vec![obj(vec![]), obj(vec![]), obj(vec![])]);
let pick = Accessor::predicate(|_, sel| matches!(sel, Selector::Index(0)));
let actual = run(from.clone(), &[pick, key("value")], constant(n(123.0)));
assert_eq!(
actual,
arr(vec![
obj(vec![("value", n(123.0))]),
obj(vec![]),
obj(vec![]),
]),
);
assert!(from.as_array().unwrap()[1].ptr_eq(&actual.as_array().unwrap()[1]));
assert!(from.as_array().unwrap()[2].ptr_eq(&actual.as_array().unwrap()[2]));
}
#[test]
fn map_partial_update_using_predicate_shares_others() {
let from = obj(vec![
("abc", obj(vec![])),
("def", obj(vec![])),
("ghi", obj(vec![])),
]);
let pick = Accessor::predicate(|_, sel| matches!(sel, Selector::Key(k) if *k == "abc"));
let actual = run(from.clone(), &[pick, key("value")], constant(n(123.0)));
assert_eq!(
actual,
obj(vec![
("abc", obj(vec![("value", n(123.0))])),
("def", obj(vec![])),
("ghi", obj(vec![])),
]),
);
let f = from.as_object().unwrap();
let a = actual.as_object().unwrap();
assert!(f.get("def").unwrap().ptr_eq(a.get("def").unwrap()));
assert!(f.get("ghi").unwrap().ptr_eq(a.get("ghi").unwrap()));
}
#[test]
fn array_with_non_existing_index_then_predicate_is_no_op() {
let from = arr(vec![]);
let always = Accessor::predicate(|_, _| true);
let actual = run(from.clone(), &[idx(0), always], constant(n(1.0)));
assert!(from.ptr_eq(&actual));
}
#[test]
fn map_with_non_existing_key_then_predicate_is_no_op() {
let from = obj(vec![]);
let always = Accessor::predicate(|_, _| true);
let actual = run(from.clone(), &[key("abc"), always], constant(n(1.0)));
assert!(from.ptr_eq(&actual));
}
#[test]
fn literal_into_existing_container_then_non_matching_predicate_is_no_op() {
let from = obj(vec![("a", arr(vec![n(1.0), n(2.0)]))]);
let never = Accessor::predicate(|_, _| false);
let actual = run(from.clone(), &[key("a"), never], constant(n(9.0)));
assert!(from.ptr_eq(&actual));
assert_eq!(actual, obj(vec![("a", arr(vec![n(1.0), n(2.0)]))]));
}
#[test]
fn predicate_reads_index_through_accessor() {
let from = arr(vec![n(1.0), n(2.0), n(3.0), n(4.0), n(6.0)]);
let pred = Accessor::predicate(|v, sel| {
let even_value = num(v) % 2.0 == 0.0;
let even_index = sel.as_index().is_some_and(|i| i % 2 == 0);
even_value && even_index
});
let actual = run(from, &[pred], multiply(10.0));
assert_eq!(actual, arr(vec![n(1.0), n(2.0), n(3.0), n(4.0), n(60.0)]));
}
#[test]
fn predicate_reads_key_through_accessor() {
let from = obj(vec![("keep", n(1.0)), ("hit", n(2.0))]);
let pred = Accessor::predicate(|_, sel| sel.as_key() == Some("hit"));
let actual = run(from, &[pred], multiply(10.0));
assert_eq!(actual, obj(vec![("keep", n(1.0)), ("hit", n(20.0))]));
}
#[test]
fn readme_truthiness_predicate() {
let from = arr(vec![n(1.0), n(2.0), n(3.0), n(4.0), n(5.0)]);
let pred = Accessor::predicate(|v, _| (num(v) as i64) % 2 != 0);
let actual = run(from, &[pred], multiply(10.0));
assert_eq!(actual, arr(vec![n(10.0), n(2.0), n(30.0), n(4.0), n(50.0)]),);
}
#[test]
fn readme_branch_then_key() {
let from = arr(vec![
obj(vec![("v", n(1.0))]),
obj(vec![("v", n(2.0))]),
obj(vec![("v", n(3.0))]),
]);
let always = Accessor::predicate(|_, _| true);
let actual = run(from, &[always, key("v")], multiply(10.0));
assert_eq!(
actual,
arr(vec![
obj(vec![("v", n(10.0))]),
obj(vec![("v", n(20.0))]),
obj(vec![("v", n(30.0))]),
]),
);
}