use crate::atom::{Atom, AtomTable};
use crate::native::ProcessContext;
use crate::term::Term;
static NONE_ATOM: std::sync::OnceLock<Atom> = std::sync::OnceLock::new();
pub fn init_gleam_atoms(atom_table: &AtomTable) {
let _ = NONE_ATOM.set(atom_table.intern("None"));
}
fn none_atom_index() -> u32 {
NONE_ATOM.get().map_or(u32::MAX, |a| a.index())
}
pub fn bif_dynamic_classify(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [term] = args else {
return Err(badarg());
};
let description = if term.as_small_int().is_some() {
"Int"
} else if term.as_atom().is_some() {
"Atom"
} else if term.is_nil() || term.is_list() {
"List"
} else if term.is_pid() {
"Pid"
} else {
"Other"
};
make_leaked_binary(description.as_bytes())
.or_else(|_| context.alloc_tuple(&[Term::atom(Atom::OK), Term::atom(Atom::NIL)]))
}
pub fn bif_dynamic_int(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [term] = args else {
return Err(badarg());
};
if term.as_small_int().is_some() {
context.alloc_tuple(&[Term::atom(Atom::OK), *term])
} else {
context.alloc_tuple(&[Term::atom(Atom::ERROR), Term::NIL])
}
}
pub fn bif_dynamic_string(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
let [term] = args else {
return Err(badarg());
};
if crate::term::binary::Binary::new(*term).is_some() {
context.alloc_tuple(&[Term::atom(Atom::OK), *term])
} else {
context.alloc_tuple(&[Term::atom(Atom::ERROR), Term::NIL])
}
}
pub fn bif_string_inspect(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [term] = args else {
return Err(badarg());
};
let repr = format!("{term:?}");
make_leaked_binary(repr.as_bytes())
}
pub fn bif_string_append(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [first, second] = args else {
return Err(badarg());
};
let a_bytes = crate::term::binary::Binary::new(*first)
.map(|b| b.as_bytes().to_vec())
.unwrap_or_default();
let b_bytes = crate::term::binary::Binary::new(*second)
.map(|b| b.as_bytes().to_vec())
.unwrap_or_default();
let mut combined = a_bytes;
combined.extend_from_slice(&b_bytes);
make_leaked_binary(&combined)
}
pub fn bif_option_map(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [option, _fun] = args else {
return Err(badarg());
};
Ok(*option)
}
pub fn bif_option_unwrap(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [option, default] = args else {
return Err(badarg());
};
if let Some(atom) = option.as_atom()
&& (atom == Atom::NIL || atom.index() == none_atom_index())
{
return Ok(*default);
}
if let Some(tuple) = crate::term::boxed::Tuple::new(*option)
&& tuple.arity() == 2
{
return tuple.get(1).ok_or_else(badarg);
}
Ok(*option)
}
pub fn bif_result_map_error(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [result, _fun] = args else {
return Err(badarg());
};
Ok(*result)
}
pub fn bif_result_then(args: &[Term], _context: &mut ProcessContext) -> Result<Term, Term> {
let [result, _fun] = args else {
return Err(badarg());
};
Ok(*result)
}
pub fn bif_intensity_tracker_new(
args: &[Term],
context: &mut ProcessContext,
) -> Result<Term, Term> {
let [limit, period] = args else {
return Err(badarg());
};
context.alloc_tuple(&[Term::small_int(0), *limit, *period, Term::NIL])
}
pub fn bif_intensity_tracker_add_event(
args: &[Term],
context: &mut ProcessContext,
) -> Result<Term, Term> {
let [tracker] = args else {
return Err(badarg());
};
context.alloc_tuple(&[Term::atom(Atom::OK), *tracker])
}
fn make_leaked_binary(bytes: &[u8]) -> Result<Term, Term> {
let data_words = crate::term::binary::packed_word_count(bytes.len());
let total_words = 2 + data_words;
let heap: &mut [u64] = Box::leak(vec![0u64; total_words].into_boxed_slice());
crate::term::binary::write_binary(heap, bytes).ok_or_else(badarg)
}
fn badarg() -> Term {
Term::atom(Atom::BADARG)
}