Skip to content

Commit

Permalink
Add resource_impl attribute macro
Browse files Browse the repository at this point in the history
Automatically sets the `IMPLEMENTS_*` associated constants and by
default registers the resource type.
  • Loading branch information
filmor committed Jul 7, 2024
1 parent 6101085 commit 5680985
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 7 deletions.
3 changes: 3 additions & 0 deletions rustler/src/codegen_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use crate::{Encoder, Env, OwnedBinary, Term};
// Re-export of inventory
pub use inventory;

// Re-export of resource registration
pub use crate::resource::Registration as ResourceRegistration;

// Names used by the `rustler::init!` macro or other generated code.
pub use crate::wrapper::exception::raise_exception;
pub use crate::wrapper::{
Expand Down
4 changes: 2 additions & 2 deletions rustler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ pub use nif::Nif;
pub type NifResult<T> = Result<T, Error>;

pub use rustler_codegen::{
init, nif, NifException, NifMap, NifRecord, NifStruct, NifTaggedEnum, NifTuple, NifUnitEnum,
NifUntaggedEnum,
init, nif, resource_impl, NifException, NifMap, NifRecord, NifStruct, NifTaggedEnum, NifTuple,
NifUnitEnum, NifUntaggedEnum,
};

#[cfg(feature = "serde")]
Expand Down
1 change: 1 addition & 0 deletions rustler/src/resource/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod util;
pub use arc::ResourceArc;
pub use error::ResourceInitError;
pub use monitor::Monitor;
pub use registration::Registration;
pub use traits::Resource;
use traits::ResourceExt;

Expand Down
15 changes: 14 additions & 1 deletion rustler/src/resource/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ use std::mem::MaybeUninit;
use std::ptr;

#[derive(Debug)]
struct Registration {
pub struct Registration {
get_type_id: fn() -> TypeId,
get_type_name: fn() -> &'static str,
init: ErlNifResourceTypeInit,
}

unsafe impl Sync for Registration {}

inventory::collect!(Registration);

impl<'a> Env<'a> {
/// Register a resource type, see `Registration::register`.
pub fn register<T: Resource>(&self) -> Result<(), ResourceInitError> {
Expand All @@ -34,6 +38,15 @@ impl<'a> Env<'a> {
/// `std::mem::needs_drop`). All other callbacks are only registered if `IMPLEMENTS_...` is set to
/// `true`.
impl Registration {
/// Register all resource types that have been submitted to the inventory.
pub fn register_all_collected(env: Env) -> Result<(), ResourceInitError> {
for reg in inventory::iter::<Registration>() {
reg.register(env)?;
}

Ok(())
}

/// Generate a new (pending) resource type registration.
pub const fn new<T: Resource>() -> Self {
Self {
Expand Down
5 changes: 5 additions & 0 deletions rustler_codegen/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ impl From<InitMacroInput> for proc_macro2::TokenStream {
let mut env = rustler::Env::new_init_env(&env, env);
// TODO: If an unwrap ever happens, we will unwind right into C! Fix this!
let load_info = rustler::Term::new(env, load_info);

if !rustler::codegen_runtime::ResourceRegistration::register_all_collected(env).is_ok() {
return 1;
}

#load.map_or(0, |inner| {
rustler::codegen_runtime::handle_nif_init_call(
inner, env, load_info
Expand Down
8 changes: 8 additions & 0 deletions rustler_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod init;
mod map;
mod nif;
mod record;
mod resource_impl;
mod tagged_enum;
mod tuple;
mod unit_enum;
Expand Down Expand Up @@ -400,3 +401,10 @@ pub fn nif_untagged_enum(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
untagged_enum::transcoder_decorator(&ast).into()
}

#[proc_macro_attribute]
pub fn resource_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::ItemImpl);

resource_impl::transcoder_decorator(input).into()
}
42 changes: 42 additions & 0 deletions rustler_codegen/src/resource_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::HashSet;

pub fn transcoder_decorator(mut input: syn::ItemImpl) -> TokenStream {
// Should be `Resource` but will fail somewhere else anyway if it isn't.
// let (_, _trait_path, _) = input.trait_.unwrap();
let type_path = match *input.self_ty {
syn::Type::Path(ref type_path) => type_path.clone(),
_ => panic!("Can only implement trait on concrete types"),
};

let mut to_add: HashSet<String> = HashSet::new();
let mut already_has: HashSet<String> = HashSet::new();

for item in input.items.iter() {
if let syn::ImplItem::Fn(f) = item {
to_add.insert(
format!("IMPLEMENTS_{}", f.sig.ident.to_string().to_uppercase()).to_string(),
);
}

if let syn::ImplItem::Const(f) = item {
already_has.insert(f.ident.to_string());
}
}

for add in to_add.difference(&already_has) {
let ident = syn::Ident::new(add, Span::call_site());
let impl_item: syn::ImplItem = syn::parse_quote!(const #ident: bool = true;);

input.items.push(impl_item);
}

quote!(
#input

rustler::codegen_runtime::inventory::submit!(
rustler::codegen_runtime::ResourceRegistration::new::<#type_path>()
);
)
}
9 changes: 5 additions & 4 deletions rustler_tests/native/rustler_test/src/test_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ pub struct TestMonitorResource {
inner: Mutex<TestMonitorResourceInner>,
}

#[rustler::resource_impl(register = true)]
impl Resource for TestMonitorResource {
const IMPLEMENTS_DOWN: bool = true;

fn down<'a>(&'a self, _env: Env<'a>, _pid: LocalPid, mon: Monitor) {
let mut inner = self.inner.lock().unwrap();
assert!(Some(mon) == inner.mon);
Expand All @@ -32,18 +31,20 @@ pub struct ImmutableResource {
b: u32,
}

#[rustler::resource_impl(register = false)]
impl Resource for ImmutableResource {}

pub struct WithBinaries {
a: [u8; 10],
b: Vec<u8>,
}

impl Resource for WithBinaries {}

pub fn on_load(env: Env) -> bool {
rustler::resource!(TestResource, env)
&& env.register::<WithBinaries>().is_ok()
&& env.register::<ImmutableResource>().is_ok()
&& env.register::<TestMonitorResource>().is_ok()
&& rustler::resource!(WithBinaries, env)
}

#[rustler::nif]
Expand Down

0 comments on commit 5680985

Please sign in to comment.