Skip to content

Commit

Permalink
WIP on bank
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronc committed Oct 8, 2024
1 parent 4f2e3f4 commit ba4a57a
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 72 deletions.
2 changes: 2 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ simple_time = { path = "util/simple_time", version = "0.0.1" }
ixc_core_macros = { path = "core_macros", version = "0.0.2" }
ixc_schema_macros = { path = "schema_macros", version = "0.0.2" }
state_objects_macros = { path = "state_objects_macros", version = "0.0.2" }
constcat = "0.5.1"
array-concat = "0.5.3"

[dev-dependencies]
ixc_testing = { path = "testing", version = "0.0.2" }
Expand Down
1 change: 1 addition & 0 deletions rust/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ixc_message_api = { path = "../message_api", version = "0.0.2" }
ixc_schema = { path = "../schema", version = "0.0.2" }
ixc_core_macros = { path = "../core_macros" }
allocator-api2 = "0.2.18"
constcat = "0.5.1"

[features]
default = ["std"]
Expand Down
1 change: 1 addition & 0 deletions rust/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod low_level;

pub use context::Context;
pub use events::EventBus;

use ixc_schema::value::OptionalValue;
use crate::error::{Error, ErrorMessage};

Expand Down
76 changes: 68 additions & 8 deletions rust/core/src/routes.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,66 @@
//! Routing system for message packets.

use core::mem::MaybeUninit;
use allocator_api2::alloc::Allocator;
use ixc_message_api::handler::{HandlerError, HandlerErrorCode, HostBackend};
use ixc_message_api::header::MessageSelector;
use ixc_message_api::packet::MessagePacket;

/// A router for message packets.
pub unsafe trait Router
where
Self: 'static,
pub unsafe trait Router where Self: 'static
{
/// The routes sorted by message selector.
const SORTED_ROUTES: &'static [Route<Self>];
}

/// A router for dynamic trait objects.
// pub unsafe trait DynRouter> where Self: 'static
// {
// /// The routes by message selector.
// const ROUTES: &'static [Route<Self>];
// }

/// A route for a message packet.
pub type Route<T> = (u64, fn(&T, &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), HandlerError>);
// pub type Route<T> = (u64, &'static dyn FnOnce(&T, &mut MessagePacket, &dyn HostBackend, &dyn Allocator) -> Result<(), HandlerError>);


// pub type DynRoute<T> = (u64, fn(&dyn T, &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), HandlerError>);
// pub type DynRoute<T> = (u64, for<'b, 'a:'b> fn(&'b (dyn T + 'a), &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), HandlerError>);

/// Execute a message packet on a router.
pub fn exec_route<R: Router>(r: &R, packet: &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), HandlerError> {
let res = R::SORTED_ROUTES.binary_search_by_key(&packet.header().message_selector, |(selector, _)| *selector);
pub fn exec_route<R: Router + ?Sized>(rtr: &R, packet: &mut MessagePacket, callbacks: &dyn HostBackend, allocator: &dyn Allocator) -> Result<(), HandlerError> {
match find_route(packet.header().message_selector) {
Some(rt) => {
rt.1(rtr, packet, callbacks, allocator)
}
None => {
Err(HandlerError::KnownCode(HandlerErrorCode::MessageNotHandled))
}
}
}

/// Find a route for a message selector.
pub fn find_route<R: Router + ?Sized>(sel: MessageSelector) -> Option<&'static Route<R>> {
let res = R::SORTED_ROUTES.binary_search_by_key(&sel, |(selector, _)| *selector);
match res {
Ok(idx) => {
R::SORTED_ROUTES[idx].1(r, packet, callbacks, allocator)
Some(&R::SORTED_ROUTES[idx])
}
Err(_) => {
Err(HandlerError::KnownCode(HandlerErrorCode::MessageNotHandled))
None
}
}
}

/// Sorts the routes by message selector.
pub const fn sort_routes<const N: usize, T: ?Sized>(mut arr: [Route<T>; N]) -> [Route<T>; N] {
// const bubble sort
let n = arr.len();
loop {
let mut swapped = false;
let mut i = 1;
while i < arr.len() {
while i < n {
if arr[i - 1].0 > arr[i].0 {
let left = arr[i - 1];
let right = arr[i];
Expand All @@ -52,5 +77,40 @@ pub const fn sort_routes<const N: usize, T: ?Sized>(mut arr: [Route<T>; N]) -> [
arr
}

// Concatenates two arrays of routes.
// pub const fn const_cat<T: ?Sized>(arr: &[Route<T>], arr2: &[Route<T>]) -> &'static [Route<T>] {
// const N: usize = arr.len() + arr2.len();
// let mut res: [Route::<T>; N] = [(0, |_, _, _, _| Err(HandlerError::KnownCode(HandlerErrorCode::MessageNotHandled))); arr.len() + arr2.len()];
// let mut i = 0;
// let mut j = 0;
// let mut k = 0;
// while i < arr.len() {
// res[k] = arr[i];
// i += 1;
// k += 1;
// }
// while j < arr2.len() {
// res[k] = arr2[j];
// j += 1;
// k += 1;
// }
// res
// }
//
// pub const fn const_map<T: ?Sized, U: ?Sized>(f: fn(&U) -> &T, arr: &'static [Route<T>]) -> &'static [Route<U>] {
// let mut i = 0;
// let n = arr.len();
// let mut res: [Route::<U>; n] = [(0, |_, _, _, _| Err(HandlerError::KnownCode(HandlerErrorCode::MessageNotHandled))); N];
// while i < n {
// let route = arr[i];
// let selector = route.0;
// let g = route.1;
// res[i] = (selector, |t, packet, callbacks, allocator| g(f(t), packet, callbacks, allocator));
// i += 1;
// }
// res
// }


// TODO: can use https://docs.rs/array-concat/latest/array_concat/ to concat arrays then the above function to sort
// or https://docs.rs/constcat/latest/constcat/
114 changes: 85 additions & 29 deletions rust/core_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,13 @@ pub fn handler(attr: TokenStream2, mut item: ItemMod) -> manyhow::Result<TokenSt
let HandlerArgs(handler) = deluxe::parse2(attr)?;
let items = &mut item.content.as_mut().unwrap().1;

let mut publish_targets = vec![];
let mut publish_fns = vec![];
let mut publish_traits = vec![];
for item in items.iter_mut() {
collect_publish_targets(&handler, item, &mut publish_targets)?;
collect_publish_targets(&handler, item, &mut publish_fns, &mut publish_traits)?;
}

push_item(items, quote! {
impl ::ixc_message_api::handler::RawHandler for #handler {
fn handle(&self, message_packet: &mut ::ixc_message_api::packet::MessagePacket, callbacks: &dyn ixc_message_api::handler::HostBackend, allocator: &dyn ::ixc_message_api::handler::Allocator) -> ::core::result::Result<(), ::ixc_message_api::handler::HandlerError> {
::ixc_core::routes::exec_route(self, message_packet, callbacks, allocator)
}
}
})?;

let mut builder = APIBuilder::default();
for publish_target in publish_targets.iter() {
for publish_target in publish_fns.iter() {
derive_api_method(&handler, &quote! {#handler}, publish_target, &mut builder)?;
}

Expand All @@ -48,15 +40,16 @@ pub fn handler(attr: TokenStream2, mut item: ItemMod) -> manyhow::Result<TokenSt
Some(msg) => quote! {#msg},
None => quote! {()},
};
let create_msg_lifetime = &builder.create_msg_lifetime;
push_item(items, quote! {
impl ::ixc_core::handler::Handler for #handler {
const NAME: &'static str = stringify!(#handler);
type Init<'a> = #on_create_msg;
type Init<'a> = #on_create_msg #create_msg_lifetime;
}
})?;

push_item(items, quote! {
impl <'a> ::ixc_core::handler::InitMessage<'a> for #on_create_msg {
impl <'a> ::ixc_core::handler::InitMessage<'a> for #on_create_msg #create_msg_lifetime {
type Handler = #handler;
type Codec = ::ixc_schema::binary::NativeBinaryCodec;
}
Expand All @@ -72,6 +65,33 @@ pub fn handler(attr: TokenStream2, mut item: ItemMod) -> manyhow::Result<TokenSt
}
})?;

// TODO it would nice to be able to combine the routes rather than needing to check one by one
let mut trait_routers = vec![];
for publish_trait in publish_traits.iter() {
let trait_ident = &publish_trait.ident;
trait_routers.push(quote!{
if let Some(rt) = ::ixc_core::routes::find_route::<dyn #trait_ident>(sel) {
return rt.1(self, message_packet, callbacks, allocator)
}
})
}

push_item(items, quote! {
impl ::ixc_message_api::handler::RawHandler for #handler {
fn handle(&self, message_packet: &mut ::ixc_message_api::packet::MessagePacket, callbacks: &dyn ixc_message_api::handler::HostBackend, allocator: &dyn ::ixc_message_api::handler::Allocator) -> ::core::result::Result<(), ::ixc_message_api::handler::HandlerError> {
let sel = message_packet.header().message_selector;
if let Some(rt) = ::ixc_core::routes::find_route(sel) {
return rt.1(self, message_packet, callbacks, allocator)
}

#(#trait_routers)*

Err(::ixc_message_api::handler::HandlerError::KnownCode(::ixc_message_api::handler::HandlerErrorCode::MessageNotHandled))
}
}
})?;


items.append(&mut builder.items);

let expanded = quote! {
Expand All @@ -85,7 +105,7 @@ fn push_item(items: &mut Vec<Item>, expanded: TokenStream2) -> manyhow::Result<(
Ok(())
}

fn collect_publish_targets(self_name: &syn::Ident, item: &mut Item, targets: &mut Vec<PublishFn>) -> manyhow::Result<()> {
fn collect_publish_targets(self_name: &syn::Ident, item: &mut Item, targets: &mut Vec<PublishFn>, traits: &mut Vec<PublishTrait>) -> manyhow::Result<()> {
match item {
Item::Impl(imp) => {
match imp.self_ty.borrow() {
Expand All @@ -98,9 +118,17 @@ fn collect_publish_targets(self_name: &syn::Ident, item: &mut Item, targets: &mu
return Ok(());
}

let publish_all = maybe_extract_attribute(imp)?;

// TODO check for trait implementation
if imp.trait_.is_some() && publish_all.is_some() {
let trait_ident = imp.trait_.as_ref().unwrap().1.segments.first().unwrap().ident.clone();
traits.push(PublishTrait {
ident: trait_ident,
});
return Ok(());
}

let publish_all = maybe_extract_attribute(imp)?;
for item in &mut imp.items {
match item {
syn::ImplItem::Fn(impl_fn) => {
Expand Down Expand Up @@ -169,6 +197,11 @@ struct PublishFn {
attrs: Vec<Attribute>,
}

#[derive(Debug)]
struct PublishTrait {
ident: Ident,
}

/// This publishes a trait or struct impl block or a single fn within an impl block.
#[manyhow]
#[proc_macro_attribute]
Expand Down Expand Up @@ -201,20 +234,26 @@ pub fn handler_api(attr: TokenStream2, mut item_trait: ItemTrait) -> manyhow::Re
let client_ident = format_ident!("{}Client", trait_ident);
builder.define_client(&client_ident)?;
builder.define_client_factory(&client_ident, &quote! {#client_ident})?;
//builder.define_client_factory(&client_ident, &dyn_trait)?;
builder.define_client_factory(&client_ident, &dyn_trait)?;
let items = &mut builder.items;
let routes = &builder.routes;
Ok(quote! {
#item_trait

#(#items)*

// unsafe impl ::ixc_core::routes::Router for dyn #trait_ident {
// const SORTED_ROUTES: &'static [::ixc_core::routes::Route<Self>] =
// &::ixc_core::routes::sort_routes([
// #(#routes)*
// ]);
// }
unsafe impl ::ixc_core::routes::Router for dyn #trait_ident {
const SORTED_ROUTES: &'static [::ixc_core::routes::Route<Self>] =
&::ixc_core::routes::sort_routes([
#(#routes)*
]);
}

impl ::ixc_message_api::handler::RawHandler for dyn #trait_ident {
fn handle(&self, message_packet: &mut ::ixc_message_api::packet::MessagePacket, callbacks: &dyn ixc_message_api::handler::HostBackend, allocator: &dyn ::ixc_message_api::handler::Allocator) -> ::core::result::Result<(), ::ixc_message_api::handler::HandlerError> {
::ixc_core::routes::exec_route(self, message_packet, callbacks, allocator)
}
}
})
}

Expand All @@ -224,6 +263,7 @@ struct APIBuilder {
routes: Vec<TokenStream2>,
client_methods: Vec<TokenStream2>,
create_msg_name: Option<Ident>,
create_msg_lifetime: TokenStream2,
}

impl APIBuilder {
Expand All @@ -237,6 +277,12 @@ impl APIBuilder {
self.0
}
}
})?;
let client_methods = &self.client_methods;
push_item(&mut self.items, quote! {
impl #client_ident {
#(#client_methods)*
}
})
}

Expand Down Expand Up @@ -318,6 +364,11 @@ fn derive_api_method(handler_ident: &Ident, handler_ty: &TokenStream2, publish_t
} else {
quote! {}
};
let opt_underscore_lifetime = if have_lifetimes {
quote! { <'_> }
} else {
quote! {}
};
push_item(&mut builder.items, quote! {
#[derive(::ixc_schema_macros::SchemaValue)]
#[sealed]
Expand All @@ -344,13 +395,15 @@ fn derive_api_method(handler_ident: &Ident, handler_ty: &TokenStream2, publish_t
ensure!(context_name.is_some(), "no context parameter found");
let context_name = context_name.unwrap();
builder.routes.push(quote! {
(< #msg_struct_name as ::ixc_core::message::Message >::SELECTOR, |h: & #handler_ty, packet, cb, a| {
(< #msg_struct_name #opt_underscore_lifetime as ::ixc_core::message::Message >::SELECTOR, |h: &Self, packet, cb, a| {
unsafe {
let cdc = < #msg_struct_name as ::ixc_core::message::Message >::Codec::default();
let cdc = < #msg_struct_name as ::ixc_core::message::Message<'_> >::Codec::default();
let in1 = packet.header().in_pointer1.get(packet);
let mut ctx = ::ixc_core::Context::new(packet.header().context_info, cb);
let #msg_struct_name { #(#msg_deconstruct)* } = ::ixc_schema::codec::decode_value::< #msg_struct_name >(&cdc, in1, ctx.memory_manager()).map_err(|e| ::ixc_message_api::handler::HandlerError::Custom(0))?;
let res = h.#fn_name(&ctx, #(#fn_ctr_args)*).map_err(|e| ::ixc_message_api::handler::HandlerError::Custom(0))?;
// NOTE: transmuting here is probably safe because there's nothing really to mutate, but ideally we should find
// a better way
let res = h.#fn_name(core::mem::transmute(&ctx), #(#fn_ctr_args)*).map_err(|e| ::ixc_message_api::handler::HandlerError::Custom(0))?;
::ixc_core::low_level::encode_optional_to_out1::< < #msg_struct_name as ::ixc_core::message::Message<'_> >::Response<'_> >(&cdc, &res, a, packet).map_err(|e| ::ixc_message_api::handler::HandlerError::Custom(0))
}
}),
Expand All @@ -365,17 +418,20 @@ fn derive_api_method(handler_ident: &Ident, handler_ty: &TokenStream2, publish_t
});
} else {
builder.routes.push(quote! {
(::ixc_core::account_api::ON_CREATE_SELECTOR, | h: & #handler_ty, packet, cb, a| {
(::ixc_core::account_api::ON_CREATE_SELECTOR, | h: &Self, packet, cb, a| {
unsafe {
let cdc = < #msg_struct_name as::ixc_core::handler::InitMessage >::Codec::default();
let cdc = < #msg_struct_name #opt_underscore_lifetime as::ixc_core::handler::InitMessage<'_> >::Codec::default();
let in1 = packet.header().in_pointer1.get(packet);
let mut ctx =::ixc_core::Context::new(packet.header().context_info, cb);
let #msg_struct_name { #(#msg_deconstruct)* } = ::ixc_schema::codec::decode_value::< #msg_struct_name > ( & cdc, in1, ctx.memory_manager()).map_err( | e|::ixc_message_api::handler::HandlerError::Custom(0)) ?;
h.#fn_name(& mut ctx, #(#fn_ctr_args)*).map_err( | e |::ixc_message_api::handler::HandlerError::Custom(0))
// NOTE: transmuting here is probably safe because there's nothing really to mutate, but ideally we should find
// a better way
h.#fn_name(core::mem::transmute(&ctx), #(#fn_ctr_args)*).map_err( | e |::ixc_message_api::handler::HandlerError::Custom(0))
}
}),}
);
builder.create_msg_name = Some(msg_struct_name);
builder.create_msg_lifetime = opt_lifetime;
}
Ok(())
}
Expand Down
Loading

0 comments on commit ba4a57a

Please sign in to comment.