use rustler::{Encoder, Env, Term};
pub fn binary<'a>(env: Env<'a>, bytes: &[u8]) -> Term<'a> {
if let Some(mut out) = rustler::types::binary::OwnedBinary::new(bytes.len()) {
out.as_mut_slice().copy_from_slice(bytes);
out.release(env).encode(env)
} else {
"".encode(env)
}
}
pub fn try_binary<'a>(env: Env<'a>, bytes: &[u8]) -> Result<Term<'a>, String> {
let Some(mut out) = rustler::types::binary::OwnedBinary::new(bytes.len()) else {
return Err("failed to allocate native plot page binary".to_owned());
};
out.as_mut_slice().copy_from_slice(bytes);
Ok(out.release(env).encode(env))
}
pub fn encode_output_map<'a>(env: Env<'a>, output: &crate::work::EvalOutput) -> Term<'a> {
map_from_terms(
env,
&[
crate::atoms::stdout().encode(env),
crate::atoms::messages().encode(env),
crate::atoms::warnings().encode(env),
],
&[
binary(env, &output.stdout),
binary(env, &output.messages),
binary(env, &output.warnings),
],
)
}
pub fn encode_eval_success<'a>(env: Env<'a>, success: crate::work::EvalSuccess) -> Term<'a> {
let result = match success.result {
Some(resource) => resource.encode(env),
None => crate::atoms::nil().encode(env),
};
let globals = success
.globals
.into_iter()
.map(|(name, resource)| (binary(env, &name), resource.encode(env)))
.collect::<Vec<_>>();
(
crate::atoms::ok(),
(result, globals, encode_output_map(env, &success.output)),
)
.encode(env)
}
pub fn encode_plot_success<'a>(
env: Env<'a>,
success: crate::work::PlotSuccess,
) -> Result<Term<'a>, String> {
let pages = success
.pages
.iter()
.map(|page| try_binary(env, page))
.collect::<Result<Vec<_>, _>>()?;
Ok((
crate::atoms::ok(),
(
success.width,
success.height,
pages,
encode_output_map(env, &success.output),
),
)
.encode(env))
}
pub fn encode_structured_error<'a>(env: Env<'a>, error: crate::work::StructuredError) -> Term<'a> {
let class_terms = error
.r_class
.iter()
.map(|class| binary(env, class))
.collect::<Vec<_>>();
let call = match error.call {
Some(call) => binary(env, &call),
None => crate::atoms::nil().encode(env),
};
let map = map_from_terms(
env,
&[
crate::atoms::message().encode(env),
crate::atoms::r_class().encode(env),
crate::atoms::call().encode(env),
crate::atoms::traceback().encode(env),
crate::atoms::output().encode(env),
],
&[
binary(env, &error.message),
class_terms.encode(env),
call,
Vec::<Term>::new().encode(env),
encode_output_map(env, &error.output),
],
);
(crate::atoms::error(), map).encode(env)
}
pub fn encode_init_config<'a>(env: Env<'a>, config: &crate::work::InitConfig) -> Term<'a> {
let lib_paths = config
.lib_paths
.iter()
.map(|path| path.as_str())
.collect::<Vec<_>>();
map_from_terms(
env,
&[
crate::atoms::r_home().encode(env),
crate::atoms::lib_r_path().encode(env),
crate::atoms::lib_paths().encode(env),
],
&[
config.r_home.as_str().encode(env),
config.lib_r_path.as_str().encode(env),
lib_paths.encode(env),
],
)
}
pub fn encode_init_failure<'a>(env: Env<'a>, failure: &crate::work::InitFailure) -> Term<'a> {
map_from_terms(
env,
&[
crate::atoms::stage().encode(env),
crate::atoms::message().encode(env),
crate::atoms::retryable().encode(env),
crate::atoms::restart_required().encode(env),
],
&[
stage_atom(env, failure.stage),
failure.message.as_str().encode(env),
failure.retryable.encode(env),
failure.restart_required.encode(env),
],
)
}
pub fn encode_init_mismatch<'a>(env: Env<'a>, mismatch: &crate::work::InitMismatch) -> Term<'a> {
let mismatches = mismatch
.mismatches
.iter()
.map(|name| field_atom(env, name))
.collect::<Vec<_>>();
map_from_terms(
env,
&[
crate::atoms::message().encode(env),
crate::atoms::mismatches().encode(env),
crate::atoms::current().encode(env),
crate::atoms::requested().encode(env),
crate::atoms::restart_required().encode(env),
],
&[
mismatch.message.as_str().encode(env),
mismatches.encode(env),
encode_init_config(env, &mismatch.current),
encode_init_config(env, &mismatch.requested),
true.encode(env),
],
)
}
pub fn encode_native_diagnostics<'a>(
env: Env<'a>,
diagnostics: crate::work::NativeDiagnostics,
) -> Term<'a> {
map_from_terms(
env,
&[
crate::atoms::init_state().encode(env),
crate::atoms::init_config().encode(env),
crate::atoms::attempted_init_config().encode(env),
crate::atoms::last_init_error().encode(env),
crate::atoms::init_attempt_count().encode(env),
crate::atoms::init_mismatch_count().encode(env),
crate::atoms::owner_thread_id().encode(env),
crate::atoms::release_enqueued_count().encode(env),
crate::atoms::release_success_count().encode(env),
crate::atoms::release_failure_count().encode(env),
crate::atoms::release_skipped_uninitialized_count().encode(env),
crate::atoms::last_release_thread_id().encode(env),
crate::atoms::last_release_resource_id().encode(env),
crate::atoms::last_release_error().encode(env),
],
&[
init_state_atom(env, diagnostics.init_state),
optional_config(env, diagnostics.init_config.as_ref()),
optional_config(env, diagnostics.attempted_init_config.as_ref()),
optional_failure(env, diagnostics.last_init_error.as_ref()),
diagnostics.init_attempt_count.encode(env),
diagnostics.init_mismatch_count.encode(env),
diagnostics.owner_thread_id.encode(env),
diagnostics.release_enqueued_count.encode(env),
diagnostics.release_success_count.encode(env),
diagnostics.release_failure_count.encode(env),
diagnostics.release_skipped_uninitialized_count.encode(env),
diagnostics.last_release_thread_id.encode(env),
optional_u64(env, diagnostics.last_release_resource_id),
optional_string(env, diagnostics.last_release_error.as_ref()),
],
)
}
fn field_atom<'a>(env: Env<'a>, name: &str) -> Term<'a> {
match name {
"r_home" => crate::atoms::r_home().encode(env),
"lib_r_path" => crate::atoms::lib_r_path().encode(env),
"lib_paths" => crate::atoms::lib_paths().encode(env),
_ => name.encode(env),
}
}
fn stage_atom<'a>(env: Env<'a>, stage: &str) -> Term<'a> {
match stage {
"dlopen_lib_r" => crate::atoms::dlopen_lib_r().encode(env),
"resolve_boot_symbols" => crate::atoms::resolve_boot_symbols().encode(env),
"rf_initialize_r" => crate::atoms::rf_initialize_r().encode(env),
"setup_rmainloop" => crate::atoms::setup_rmainloop().encode(env),
"load_r_api" => crate::atoms::load_r_api().encode(env),
"apply_lib_paths" => crate::atoms::apply_lib_paths().encode(env),
"ensure_eval_helper" => crate::atoms::ensure_eval_helper().encode(env),
_ => stage.encode(env),
}
}
fn init_state_atom<'a>(env: Env<'a>, state: crate::work::InitState) -> Term<'a> {
match state {
crate::work::InitState::Uninitialized => crate::atoms::uninitialized().encode(env),
crate::work::InitState::Initialized => crate::atoms::initialized().encode(env),
crate::work::InitState::Failed => crate::atoms::failed().encode(env),
}
}
fn optional_config<'a>(env: Env<'a>, config: Option<&crate::work::InitConfig>) -> Term<'a> {
match config {
Some(config) => encode_init_config(env, config),
None => crate::atoms::nil().encode(env),
}
}
fn optional_failure<'a>(env: Env<'a>, failure: Option<&crate::work::InitFailure>) -> Term<'a> {
match failure {
Some(failure) => encode_init_failure(env, failure),
None => crate::atoms::nil().encode(env),
}
}
fn optional_u64<'a>(env: Env<'a>, value: Option<u64>) -> Term<'a> {
match value {
Some(value) => value.encode(env),
None => crate::atoms::nil().encode(env),
}
}
fn optional_string<'a>(env: Env<'a>, value: Option<&String>) -> Term<'a> {
match value {
Some(value) => value.as_str().encode(env),
None => crate::atoms::nil().encode(env),
}
}
pub fn map_from_terms<'a>(env: Env<'a>, keys: &[Term<'a>], values: &[Term<'a>]) -> Term<'a> {
Term::map_from_term_arrays(env, keys, values).unwrap_or_else(|_| Term::map_new(env))
}