use crate::objects::value::{JsValue, NativeIterator};
use super::util::same_value_zero;
fn normalize_negative_zero(v: JsValue) -> JsValue {
if let JsValue::HeapNumber(n) = &v
&& *n == 0.0
&& n.is_sign_negative()
{
return JsValue::HeapNumber(0.0);
}
v
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetIteratorKind {
Values,
Entries,
Keys,
}
#[derive(Debug, Clone, Default)]
pub struct JsSet {
values: Vec<Option<JsValue>>,
size: usize,
}
fn set_find_value_index(set: &JsSet, value: &JsValue) -> Option<usize> {
set.values.iter().position(|entry| {
entry
.as_ref()
.is_some_and(|candidate| same_value_zero(candidate, value))
})
}
pub fn set_next_value(set: &JsSet, cursor: &mut usize) -> Option<JsValue> {
while let Some(value) = set.values.get(*cursor) {
*cursor += 1;
if let Some(value) = value {
return Some(value.clone());
}
}
None
}
pub fn set_next_iteration_item(
set: &JsSet,
cursor: &mut usize,
kind: SetIteratorKind,
) -> Option<JsValue> {
let value = set_next_value(set, cursor)?;
Some(match kind {
SetIteratorKind::Values | SetIteratorKind::Keys => value.clone(),
SetIteratorKind::Entries => JsValue::new_array(vec![value.clone(), value]),
})
}
pub fn set_new() -> JsSet {
JsSet::default()
}
pub fn set_from_iterable(items: Vec<JsValue>) -> JsSet {
let mut s = set_new();
for v in items {
set_add(&mut s, v);
}
s
}
pub fn set_add(set: &mut JsSet, value: JsValue) {
let value = normalize_negative_zero(value);
if set_find_value_index(set, &value).is_none() {
set.values.push(Some(value));
set.size += 1;
}
}
pub fn set_has(set: &JsSet, value: &JsValue) -> bool {
set.values
.iter()
.filter_map(Option::as_ref)
.any(|v| same_value_zero(v, value))
}
pub fn set_delete(set: &mut JsSet, value: &JsValue) -> bool {
if let Some(pos) = set_find_value_index(set, value) {
set.values[pos] = None;
set.size -= 1;
true
} else {
false
}
}
pub fn set_clear(set: &mut JsSet) {
for value in &mut set.values {
*value = None;
}
set.size = 0;
}
pub fn set_size(set: &JsSet) -> usize {
set.size
}
pub fn set_for_each(set: &JsSet, mut callback: impl FnMut(&JsValue)) {
for v in set.values.iter().flatten() {
callback(v);
}
}
pub fn set_values(set: &JsSet) -> Vec<JsValue> {
set.values.iter().flatten().cloned().collect()
}
pub fn set_keys(set: &JsSet) -> Vec<JsValue> {
set_values(set)
}
pub fn set_entries(set: &JsSet) -> Vec<(JsValue, JsValue)> {
set.values
.iter()
.flatten()
.map(|v| (v.clone(), v.clone()))
.collect()
}
pub fn set_iter(set: &JsSet) -> Vec<JsValue> {
set_values(set)
}
pub fn set_create_iterator(set: &JsSet, kind: SetIteratorKind) -> JsValue {
let items: Vec<JsValue> = match kind {
SetIteratorKind::Values | SetIteratorKind::Keys => {
set.values.iter().flatten().cloned().collect()
}
SetIteratorKind::Entries => set
.values
.iter()
.flatten()
.map(|v| JsValue::new_array(vec![v.clone(), v.clone()]))
.collect(),
};
JsValue::Iterator(NativeIterator::from_items(items))
}
pub fn set_union(a: &JsSet, b: &JsSet) -> JsSet {
let mut result = a.clone();
for v in b.values.iter().flatten() {
if !set_has(&result, v) {
result.values.push(Some(v.clone()));
result.size += 1;
}
}
result
}
pub fn set_intersection(a: &JsSet, b: &JsSet) -> JsSet {
let mut result = set_new();
for v in a.values.iter().flatten() {
if set_has(b, v) {
result.values.push(Some(v.clone()));
result.size += 1;
}
}
result
}
pub fn set_difference(a: &JsSet, b: &JsSet) -> JsSet {
let mut result = set_new();
for v in a.values.iter().flatten() {
if !set_has(b, v) {
result.values.push(Some(v.clone()));
result.size += 1;
}
}
result
}
pub fn set_symmetric_difference(a: &JsSet, b: &JsSet) -> JsSet {
let mut result = set_new();
for v in a.values.iter().flatten() {
if !set_has(b, v) {
result.values.push(Some(v.clone()));
result.size += 1;
}
}
for v in b.values.iter().flatten() {
if !set_has(a, v) {
result.values.push(Some(v.clone()));
result.size += 1;
}
}
result
}
pub fn set_is_subset_of(a: &JsSet, b: &JsSet) -> bool {
a.values.iter().flatten().all(|v| set_has(b, v))
}
pub fn set_is_superset_of(a: &JsSet, b: &JsSet) -> bool {
b.values.iter().flatten().all(|v| set_has(a, v))
}
pub fn set_is_disjoint_from(a: &JsSet, b: &JsSet) -> bool {
!a.values.iter().flatten().any(|v| set_has(b, v))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_set_from_iterable() {
let items = vec![JsValue::Smi(1), JsValue::Smi(2), JsValue::Smi(3)];
let s = set_from_iterable(items);
assert_eq!(set_size(&s), 3);
assert!(set_has(&s, &JsValue::Smi(1)));
assert!(set_has(&s, &JsValue::Smi(3)));
}
#[test]
fn test_set_from_iterable_deduplicates() {
let items = vec![JsValue::Smi(1), JsValue::Smi(1), JsValue::Smi(2)];
let s = set_from_iterable(items);
assert_eq!(set_size(&s), 2);
}
#[test]
fn test_set_keys_equals_values() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(10));
set_add(&mut s, JsValue::Smi(20));
assert_eq!(set_keys(&s), set_values(&s));
}
#[test]
fn test_set_entries_returns_value_value_pairs() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
assert_eq!(
set_entries(&s),
vec![
(JsValue::Smi(1), JsValue::Smi(1)),
(JsValue::Smi(2), JsValue::Smi(2)),
]
);
}
#[test]
fn test_set_iter_returns_values() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(7));
set_add(&mut s, JsValue::Smi(3));
assert_eq!(set_iter(&s), set_values(&s));
}
#[test]
fn test_set_add_unique_values() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
assert_eq!(set_size(&s), 2);
}
#[test]
fn test_set_add_ignores_duplicates() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(1));
assert_eq!(set_size(&s), 1);
}
#[test]
fn test_set_has_present_value() {
let mut s = set_new();
set_add(&mut s, JsValue::Boolean(false));
assert!(set_has(&s, &JsValue::Boolean(false)));
}
#[test]
fn test_set_has_absent_value() {
let s = set_new();
assert!(!set_has(&s, &JsValue::Null));
}
#[test]
fn test_set_delete_existing_value() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(42));
assert!(set_delete(&mut s, &JsValue::Smi(42)));
assert_eq!(set_size(&s), 0);
}
#[test]
fn test_set_delete_missing_value_returns_false() {
let mut s = set_new();
assert!(!set_delete(&mut s, &JsValue::Smi(1)));
}
#[test]
fn test_set_clear() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
set_clear(&mut s);
assert_eq!(set_size(&s), 0);
}
#[test]
fn test_set_insertion_order_preserved() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(3));
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
assert_eq!(
set_values(&s),
vec![JsValue::Smi(3), JsValue::Smi(1), JsValue::Smi(2)]
);
}
#[test]
fn test_set_for_each_visits_all_values() {
let mut s = set_new();
set_add(&mut s, JsValue::Smi(10));
set_add(&mut s, JsValue::Smi(20));
let mut out = Vec::new();
set_for_each(&s, |v| out.push(v.clone()));
assert_eq!(out, vec![JsValue::Smi(10), JsValue::Smi(20)]);
}
#[test]
fn test_set_nan_deduplication() {
let mut s = set_new();
set_add(&mut s, JsValue::HeapNumber(f64::NAN));
set_add(&mut s, JsValue::HeapNumber(f64::NAN));
assert_eq!(set_size(&s), 1);
assert!(set_has(&s, &JsValue::HeapNumber(f64::NAN)));
}
#[test]
fn test_set_negative_zero_deduplication() {
let mut s = set_new();
set_add(&mut s, JsValue::HeapNumber(0.0_f64));
set_add(&mut s, JsValue::HeapNumber(-0.0_f64));
assert_eq!(set_size(&s), 1);
}
#[test]
fn test_set_create_iterator_values() {
use crate::builtins::iterator::iterator_next;
let mut s = set_new();
set_add(&mut s, JsValue::Smi(10));
set_add(&mut s, JsValue::Smi(20));
let iter = set_create_iterator(&s, SetIteratorKind::Values);
assert_eq!(iterator_next(&iter).unwrap().value, JsValue::Smi(10));
assert_eq!(iterator_next(&iter).unwrap().value, JsValue::Smi(20));
assert!(iterator_next(&iter).unwrap().done);
}
#[test]
fn test_set_create_iterator_keys_same_as_values() {
use crate::builtins::iterator::iterator_next;
let mut s = set_new();
set_add(&mut s, JsValue::Smi(5));
let keys_iter = set_create_iterator(&s, SetIteratorKind::Keys);
let vals_iter = set_create_iterator(&s, SetIteratorKind::Values);
assert_eq!(
iterator_next(&keys_iter).unwrap().value,
iterator_next(&vals_iter).unwrap().value
);
}
#[test]
fn test_set_create_iterator_entries() {
use crate::builtins::iterator::iterator_next;
let mut s = set_new();
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
let iter = set_create_iterator(&s, SetIteratorKind::Entries);
let r1 = iterator_next(&iter).unwrap();
if let JsValue::Array(arr) = &r1.value {
assert_eq!(*arr.borrow(), vec![JsValue::Smi(1), JsValue::Smi(1)]);
} else {
panic!("expected Array");
}
let r2 = iterator_next(&iter).unwrap();
if let JsValue::Array(arr) = &r2.value {
assert_eq!(*arr.borrow(), vec![JsValue::Smi(2), JsValue::Smi(2)]);
} else {
panic!("expected Array");
}
assert!(iterator_next(&iter).unwrap().done);
}
#[test]
fn test_set_create_iterator_empty() {
use crate::builtins::iterator::iterator_next;
let s = set_new();
let iter = set_create_iterator(&s, SetIteratorKind::Values);
assert!(iterator_next(&iter).unwrap().done);
}
#[test]
fn test_set_create_iterator_insertion_order() {
use crate::builtins::iterator::iterator_next;
let mut s = set_new();
set_add(&mut s, JsValue::Smi(3));
set_add(&mut s, JsValue::Smi(1));
set_add(&mut s, JsValue::Smi(2));
let iter = set_create_iterator(&s, SetIteratorKind::Values);
assert_eq!(iterator_next(&iter).unwrap().value, JsValue::Smi(3));
assert_eq!(iterator_next(&iter).unwrap().value, JsValue::Smi(1));
assert_eq!(iterator_next(&iter).unwrap().value, JsValue::Smi(2));
assert!(iterator_next(&iter).unwrap().done);
}
}