Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use std::collections::HashSet;
/// Largest integer JavaScript can hold exactly; beyond it the three languages disagree.
const SAFE_INTEGER: i64 = 9007199254740991;
/// The identity of a record, or `None` when it has none.
///
/// Rendered exactly as collections.group-by-key renders a group name, so the
/// two capabilities agree on what "the same key" means.
///
/// # Panics
/// Panics on a float, a list or a map, or an integer outside the safe range.
fn key_of(value: &Value, key: &str) -> Option<String> {
match value {
// A record with no key is not a duplicate of anything: two records
// that both lack an id are two unknowns, not one thing seen twice.
Value::Null => None,
Value::Str(s) => Some(s.clone()),
Value::Bool(b) => Some((if *b { "true" } else { "false" }).to_string()),
Value::Int(i) => {
if i.abs() > SAFE_INTEGER {
panic!("cannot dedupe by the out-of-range number {} at \"{}\"", i, key);
}
Some(i.to_string())
}
Value::Float(f) => panic!("cannot dedupe by the fractional number {} at \"{}\"", f, key),
_ => panic!("cannot dedupe by the list or map at \"{}\"", key),
}
}
/// Remove records that share the value at `key`, keeping the first or the
/// last of each set.
///
/// Survivors keep their own positions: with "last", the survivor sits where
/// the last occurrence was, which is what "latest wins" means in a change feed.
///
/// # Panics
/// Panics on an empty key, an unknown `keep`, or an ungroupable key value.
pub fn dedupe_by_key(records: &[Value], key: &str, keep: &str) -> Vec<Value> {
if key.is_empty() {
panic!("dedupe_by_key needs a non-empty key name");
}
if keep != "first" && keep != "last" {
panic!("keep must be \"first\" or \"last\", received \"{}\"", keep);
}
// Every key is rendered up front, so a bad value panics whichever mode runs.
let keys: Vec<Option<String>> = records.iter().map(|r| key_of(r.get(key), key)).collect();
let mut seen: HashSet<String> = HashSet::new();
let mut survives = vec![false; records.len()];
let mut order: Vec<usize> = (0..records.len()).collect();
// Walking backwards for "last" makes the last occurrence the first one seen.
if keep == "last" {
order.reverse();
}
for i in order {
match &keys[i] {
None => survives[i] = true,
Some(k) => {
if seen.insert(k.clone()) {
survives[i] = true;
}
}
}
}
records
.iter()
.enumerate()
.filter(|(i, _)| survives[*i])
.map(|(_, r)| r.clone())
.collect()
}
pub fn fune_vector(args: &[Value]) -> Value {
Value::Arr(dedupe_by_key(args[0].as_arr(), args[1].as_str(), args[2].as_str()))
}