use crate::atom::Atom;
use crate::native::ProcessContext;
use crate::term::Term;
use crate::term::boxed::{Cons, Map, Tuple};
use crate::term::compare;
pub fn bif_maps_from_list(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let pairs = list_of_2tuples(*input)?;
let mut entries: Vec<(Term, Term)> = Vec::with_capacity(pairs.len());
for (key, value) in pairs {
if let Some(existing) = entries.iter_mut().find(|(k, _)| *k == key) {
existing.1 = value;
} else {
entries.push((key, value));
}
}
let atom_table = context.atom_table().ok_or_else(badarg)?;
entries.sort_by(|(a, _), (b, _)| compare::cmp(*a, *b, atom_table));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
context.alloc_map(&keys, &values)
}
pub fn bif_maps_merge(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [map1_term, map2_term] = args else {
return Err(badarg());
};
let map1 = Map::new(*map1_term).ok_or_else(badarg)?;
let map2 = Map::new(*map2_term).ok_or_else(badarg)?;
let mut entries: Vec<(Term, Term)> = Vec::with_capacity(map1.len() + map2.len());
for i in 0..map1.len() {
if let (Some(k), Some(v)) = (map1.key(i), map1.value(i)) {
entries.push((k, v));
}
}
for i in 0..map2.len() {
if let (Some(k), Some(v)) = (map2.key(i), map2.value(i)) {
if let Some(existing) = entries.iter_mut().find(|(ek, _)| *ek == k) {
existing.1 = v;
} else {
entries.push((k, v));
}
}
}
let atom_table = context.atom_table().ok_or_else(badarg)?;
entries.sort_by(|(a, _), (b, _)| compare::cmp(*a, *b, atom_table));
let keys: Vec<Term> = entries.iter().map(|(k, _)| *k).collect();
let values: Vec<Term> = entries.iter().map(|(_, v)| *v).collect();
context.alloc_map(&keys, &values)
}
pub fn bif_maps_remove(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [key_term, map_term] = args else {
return Err(badarg());
};
let map = Map::new(*map_term).ok_or_else(badarg)?;
let mut keys = Vec::with_capacity(map.len());
let mut values = Vec::with_capacity(map.len());
for i in 0..map.len() {
if let (Some(k), Some(v)) = (map.key(i), map.value(i))
&& k != *key_term
{
keys.push(k);
values.push(v);
}
}
context.alloc_map(&keys, &values)
}
pub fn bif_lists_reverse(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [input] = args else {
return Err(badarg());
};
let elements = list_to_vec(*input)?;
let reversed: Vec<_> = elements.into_iter().rev().collect();
context.alloc_list(&reversed)
}
pub fn bif_maps_map(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
super::maps_bifs::bif_maps_map(args, context)
}
pub fn bif_timer_sleep(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [ms_term] = args else {
return Err(badarg());
};
let ms = ms_term
.as_small_int()
.and_then(|v| u64::try_from(v).ok())
.ok_or_else(badarg)?;
std::thread::sleep(std::time::Duration::from_millis(ms));
Ok(Term::atom(Atom::OK))
}
fn list_of_2tuples(term: Term) -> Result<Vec<(Term, Term)>, Term> {
let mut pairs = Vec::new();
let mut current = term;
loop {
if current.is_nil() {
return Ok(pairs);
}
let cons = Cons::new(current).ok_or_else(badarg)?;
let head = cons.head();
let tuple = Tuple::new(head).ok_or_else(badarg)?;
if tuple.arity() != 2 {
return Err(badarg());
}
let key = tuple.get(0).ok_or_else(badarg)?;
let value = tuple.get(1).ok_or_else(badarg)?;
pairs.push((key, value));
current = cons.tail();
}
}
fn list_to_vec(term: Term) -> Result<Vec<Term>, Term> {
let mut elements = Vec::new();
let mut current = term;
loop {
if current.is_nil() {
return Ok(elements);
}
let cons = Cons::new(current).ok_or_else(badarg)?;
elements.push(cons.head());
current = cons.tail();
}
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}