use std::ffi::{c_char, c_void, CStr, CString};
use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};
mod abi;
pub use abi::*;
pub fn code_number(n: f64) -> CodeValue {
CodeValue {
tag: CODE_TAG_NUMBER,
number: n,
string: ptr::null(),
boolean: 0,
fields: ptr::null(),
field_count: 0,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_string(s: &str) -> CodeValue {
CodeValue {
tag: CODE_TAG_STRING,
number: 0.0,
string: leak_str(s),
boolean: 0,
fields: ptr::null(),
field_count: 0,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_string_raw(ptr: *const c_char) -> CodeValue {
CodeValue {
tag: CODE_TAG_STRING,
number: 0.0,
string: ptr,
boolean: 0,
fields: ptr::null(),
field_count: 0,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_boolean(b: bool) -> CodeValue {
CodeValue {
tag: CODE_TAG_BOOLEAN,
number: 0.0,
string: ptr::null(),
boolean: if b { 1 } else { 0 },
fields: ptr::null(),
field_count: 0,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_null() -> CodeValue {
CodeValue {
tag: CODE_TAG_NULL,
number: 0.0,
string: ptr::null(),
boolean: 0,
fields: ptr::null(),
field_count: 0,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_object(fields: Vec<CodeField>) -> CodeValue {
let boxed = fields.into_boxed_slice();
let count = boxed.len() as u32;
let ptr = Box::leak(boxed).as_ptr();
CodeValue {
tag: CODE_TAG_OBJECT,
number: 0.0,
string: ptr::null(),
boolean: 0,
fields: ptr,
field_count: count,
elements: ptr::null(),
element_count: 0,
}
}
pub fn code_array(elements: Vec<CodeValue>) -> CodeValue {
let boxed = elements.into_boxed_slice();
let count = boxed.len() as u32;
let ptr = Box::leak(boxed).as_ptr();
CodeValue {
tag: CODE_TAG_ARRAY,
number: 0.0,
string: ptr::null(),
boolean: 0,
fields: ptr::null(),
field_count: 0,
elements: ptr,
element_count: count,
}
}
pub fn code_field(name: &str, value: CodeValue) -> CodeField {
CodeField {
name: leak_str(name),
value,
}
}
pub unsafe fn read_str<'a>(v: &CodeValue) -> &'a str {
if v.tag != CODE_TAG_STRING || v.string.is_null() {
return "";
}
CStr::from_ptr(v.string).to_str().unwrap_or("")
}
pub fn read_number(v: &CodeValue) -> f64 {
if v.tag != CODE_TAG_NUMBER {
return 0.0;
}
v.number
}
pub fn read_boolean(v: &CodeValue) -> bool {
if v.tag != CODE_TAG_BOOLEAN {
return false;
}
v.boolean != 0
}
pub unsafe fn read_field<'a>(v: &CodeValue, name: &str) -> Option<&'a CodeValue> {
if v.tag != CODE_TAG_OBJECT || v.fields.is_null() {
return None;
}
for i in 0..v.field_count as usize {
let field = &*v.fields.add(i);
if !field.name.is_null() {
let field_name = CStr::from_ptr(field.name).to_str().unwrap_or("");
if field_name == name {
return Some(&field.value);
}
}
}
None
}
pub unsafe fn read_field_str<'a>(v: &CodeValue, name: &str) -> &'a str {
match read_field(v, name) {
Some(fv) => read_str(fv),
None => "",
}
}
pub unsafe fn read_field_number(v: &CodeValue, name: &str) -> f64 {
match read_field(v, name) {
Some(fv) => read_number(fv),
None => 0.0,
}
}
pub unsafe fn read_field_bool(v: &CodeValue, name: &str) -> bool {
match read_field(v, name) {
Some(fv) => read_boolean(fv),
None => false,
}
}
pub fn leak_str(s: &str) -> *const c_char {
CString::new(s)
.unwrap_or_else(|_| CString::new("").unwrap())
.into_raw() as *const c_char
}
#[doc(hidden)]
pub static EMIT_FN_PTR: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
#[doc(hidden)]
pub static EMIT_CTX_PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
pub fn code_emit(particle: CodeValue) {
let fn_ptr = EMIT_FN_PTR.load(Ordering::Acquire);
if fn_ptr.is_null() {
return;
}
let ctx = EMIT_CTX_PTR.load(Ordering::Acquire);
let func: CodeEmitFn = unsafe { std::mem::transmute(fn_ptr) };
unsafe { func(ctx, particle) };
}
pub fn code_emit_log(source: &str, level: &str, message: &str) {
code_emit(code_object(vec![
code_field("_class", code_string("Log")),
code_field("source", code_string(source)),
code_field("level", code_string(level)),
code_field("message", code_string(message)),
]));
}
pub fn code_emit_exception(source: &str, message: &str) {
code_emit(code_object(vec![
code_field("_class", code_string("Exception")),
code_field("source", code_string(source)),
code_field("message", code_string(message)),
]));
}
#[macro_export]
macro_rules! code_module {
(
vars: [ $( $var_name:literal => $var_value:expr ),* $(,)? ],
types: [ $( $type_name:literal [ $( ( $field_name:literal , $field_type:literal $( , $field_optional:literal )? ) ),* $(,)? ] ),* $(,)? ],
handlers: [ $( $handler_class:literal => $handler_fn:expr ),* $(,)? ],
emissions: [ $( $emit_class:literal => $emit_target:literal ),* $(,)? ]
$(,)?
) => {
#[no_mangle]
pub extern "C" fn code_module_abi_version() -> u32 {
$crate::CODE_ABI_VERSION
}
#[no_mangle]
pub unsafe extern "C" fn code_module_set_emit(
emit_fn: $crate::CodeEmitFn,
context: *mut std::ffi::c_void,
) {
$crate::EMIT_FN_PTR.store(emit_fn as *mut (), std::sync::atomic::Ordering::Release);
$crate::EMIT_CTX_PTR.store(context, std::sync::atomic::Ordering::Release);
}
#[no_mangle]
pub extern "C" fn code_module_init() -> *const $crate::CodeModuleDesc {
let vars: Vec<$crate::CodeExportVar> = vec![
$( $crate::CodeExportVar {
name: $crate::leak_str($var_name),
value: $var_value,
}, )*
];
let types: Vec<$crate::CodeExportType> = vec![
$( {
let fields: Vec<$crate::CodeTypeField> = vec![
$( $crate::CodeTypeField {
name: $crate::leak_str($field_name),
type_name: $crate::leak_str($field_type),
is_optional: { 0u8 $( + ($field_optional as u8) )? },
}, )*
];
let fields_leaked = Box::leak(fields.into_boxed_slice());
$crate::CodeExportType {
name: $crate::leak_str($type_name),
fields: fields_leaked.as_ptr(),
field_count: fields_leaked.len() as u32,
}
}, )*
];
let handlers: Vec<$crate::CodeExportHandler> = vec![
$( $crate::CodeExportHandler {
class_name: $crate::leak_str($handler_class),
handler: $handler_fn,
}, )*
];
let emissions: Vec<$crate::CodeEmission> = vec![
$( $crate::CodeEmission {
class_name: $crate::leak_str($emit_class),
target: {
match $emit_target {
"base" => $crate::CODE_EMIT_TARGET_BASE,
_ => $crate::CODE_EMIT_TARGET_BASE, }
},
}, )*
];
let vars_leaked = Box::leak(vars.into_boxed_slice());
let types_leaked = Box::leak(types.into_boxed_slice());
let handlers_leaked = Box::leak(handlers.into_boxed_slice());
let emissions_leaked = Box::leak(emissions.into_boxed_slice());
let desc = Box::new($crate::CodeModuleDesc {
abi_version: $crate::CODE_ABI_VERSION,
vars: vars_leaked.as_ptr(),
var_count: vars_leaked.len() as u32,
handlers: handlers_leaked.as_ptr(),
handler_count: handlers_leaked.len() as u32,
types: types_leaked.as_ptr(),
type_count: types_leaked.len() as u32,
emissions: emissions_leaked.as_ptr(),
emission_count: emissions_leaked.len() as u32,
});
Box::leak(desc)
}
};
}