use std::collections::BTreeSet;
use beamr::native::NativeFn;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Mfa {
pub module: String,
pub function: String,
pub arity: u8,
}
impl Mfa {
#[must_use]
pub fn new(module: impl Into<String>, function: impl Into<String>, arity: u8) -> Self {
Self {
module: module.into(),
function: function.into(),
arity,
}
}
#[must_use]
pub fn display(&self) -> String {
format!("{}:{}/{}", self.module, self.function, self.arity)
}
}
#[derive(Clone, Debug)]
pub struct NifEntry {
pub mfa: Mfa,
pub function: NativeFn,
pub is_dirty: bool,
}
impl NifEntry {
#[must_use]
pub fn new(mfa: Mfa, function: NativeFn) -> Self {
Self {
mfa,
function,
is_dirty: false,
}
}
#[must_use]
pub fn dirty(mfa: Mfa, function: NativeFn) -> Self {
Self {
mfa,
function,
is_dirty: true,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct NifRegistration {
entries: Vec<NifEntry>,
}
#[cfg(test)]
pub(crate) fn test_native_zero(
args: &[beamr::term::Term],
context: &mut beamr::native::ProcessContext,
) -> Result<beamr::term::Term, beamr::term::Term> {
let _ = context;
if args.len() > 255 {
return Err(beamr::term::Term::small_int(0));
}
Ok(beamr::term::Term::small_int(0))
}
impl NifRegistration {
#[must_use]
pub const fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn add_host_nifs(&mut self, entries: impl IntoIterator<Item = NifEntry>) -> &mut Self {
self.entries.extend(entries);
self
}
pub fn add_engine_nifs(&mut self) -> &mut Self {
self.entries
.extend(super::engine_nifs::engine_nif_entries());
self
}
#[must_use]
pub fn module_names(&self) -> Vec<String> {
self.entries
.iter()
.map(|entry| entry.mfa.module.clone())
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
}
#[must_use]
pub fn into_entries(self) -> Vec<NifEntry> {
self.entries
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
}
#[cfg(test)]
mod tests {
use beamr::native::ProcessContext;
use beamr::term::Term;
use super::{Mfa, NifEntry, NifRegistration};
fn native_zero(args: &[Term], context: &mut ProcessContext) -> Result<Term, Term> {
super::test_native_zero(args, context)
}
#[test]
fn collects_host_and_engine_nifs() {
let mut registration = NifRegistration::new();
registration
.add_engine_nifs()
.add_host_nifs([NifEntry::dirty(Mfa::new("host", "zero", 0), native_zero)]);
assert!(registration.len() >= 2);
let module_names = registration.module_names();
assert!(module_names.iter().any(|module| module == "aion_flow_ffi"));
assert!(module_names.iter().any(|module| module == "host"));
let entries = registration.into_entries();
let host_entry = entries.iter().find(|e| e.mfa.display() == "host:zero/0");
assert!(host_entry.is_some_and(|e| e.is_dirty));
let dispatch_activity = entries
.iter()
.find(|e| e.mfa.display() == "aion_flow_ffi:dispatch_activity/3");
assert!(dispatch_activity.is_some_and(|e| !e.is_dirty));
let await_activity = entries
.iter()
.find(|e| e.mfa.display() == "aion_flow_ffi:await_activity_result/1");
assert!(await_activity.is_some_and(|e| !e.is_dirty));
}
}