Skip to content

Commit

Permalink
feat!: add the CapsuleHandle
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoryConrad committed Jul 17, 2023
1 parent 3305b32 commit 5fbc2b5
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 177 deletions.
63 changes: 20 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

<p align="center">
<img src="https://github.com/GregoryConrad/rearch-rs/blob/main/assets/tmp-banner.jpg?raw=true" width="75%" alt="Banner" />
<img src="https://github.com/GregoryConrad/rearch-docs/blob/main/assets/banner.jpg?raw=true" width="75%" alt="Banner" />
</p>

<p align="center">
Expand All @@ -14,6 +14,7 @@ rearch = re-imagined approach to application design and architecture

---


## Features
Specifically, rearch is a:
- ⚡️ Reactive
Expand All @@ -25,71 +26,47 @@ Specifically, rearch is a:

Framework.


# Under Construction
This README is a large WIP, but there are some basics here.


## Getting Started
First, you need to define some "capsules."

There are several ways to define capsules, depending on your toolchain and opinions toward macros.
Not everyone likes macros, and not everyone can use nightly;
thus, you can pick a combination of the below options that you like,
knowing that every type of capsule syntax shown below are 100% compatible with one another!

### Macros (Optional), Stable and Nightly Rust
Note: This particular example also uses the nightly-only `better-api` feature.
You can also write macros using the stable Rust syntax further below.
```rust
#[capsule]
fn count(register: SideEffectRegistrar) -> (u8, impl Fn(u8) + Clone + Send + Sync) {
let (state, set_state) = register(side_effects::state(0));
(*state, set_state)
}

#[capsule]
fn count_plus_one() -> u8 {
_count.0 + 1
}

let container = Container::new();
let ((count, set_count), count_plus_one) = container.read((count, count_plus_one));
```
There are two ways to define capsules, depending on your toolchain.
Both are 100% compatible with each other, and will continue to be in the future as well
(forward compatability was a *very* strong factor when designing the api).

### Vanilla, Nightly Rust (`better-api` feature)
The author's personal favorite syntax; however, the macro is sometimes nice to reduce some churn
(mostly in the capsule function arguments).
Once `unboxed_closures` and `fn_traits` stabilize, the nightly syntax will be the preferred syntax
(over the current stable syntax).
### Nightly Rust (`better-api` feature)
Once `unboxed_closures` and `fn_traits` stabilize,
this nightly syntax will be the preferred syntax (over the current stable syntax),
and this will no longer be feature-gated.
```rust
fn count(
_: CapsuleReader, register: SideEffectRegistrar,
) -> (u8, impl Fn(u8) + Clone + Send + Sync) {
fn count(CapsuleHandle { register, .. }: CapsuleHandle) -> (u8, impl Fn(u8) + Clone + Send + Sync) {
let (state, set_state) = register(side_effects::state(0));
(*state, set_state)
}

fn count_plus_one(mut get: CapsuleReader, _: SideEffectRegistrar) -> u8 {
get(count).0 + 1
fn count_plus_one(CapsuleHandle { mut get, .. }: CapsuleHandle) -> u8 {
get(count).0 + 1
}

let container = Container::new();
let ((count, set_count), count_plus_one) = container.read((count, count_plus_one));
```

### Vanilla, Stable Rust
Objectively the worst usability and syntax combination of them all.
### Stable Rust
Once `unboxed_closures` and `fn_traits` stabilize, the below will be deprecated in favor
of the now nightly-only syntax.
of the now nightly-only syntax (backward compatability will be maintained).
```rust
fn count(
_: CapsuleReader, registrar: SideEffectRegistrar,
) -> (u8, impl Fn(u8) + Clone + Send + Sync) {
let (state, set_state) = registrar.register(side_effects::state(0));
fn count(CapsuleHandle { register, .. }: CapsuleHandle) -> (u8, impl Fn(u8) + Clone + Send + Sync) {
let (state, set_state) = register.register(side_effects::state(0));
(*state, set_state)
}

fn count_plus_one(mut reader: CapsuleReader, _: SideEffectRegistrar) -> u8 {
reader.read(count).0 + 1
fn count_plus_one(CapsuleHandle { mut get, .. }: CapsuleHandle) -> u8 {
get.get(count).0 + 1
}

let container = Container::new();
Expand Down
Binary file removed assets/tmp-banner.jpg
Binary file not shown.
2 changes: 0 additions & 2 deletions rearch-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,5 @@ readme.workspace = true
proc-macro = true

[dependencies]
proc-macro2 = "1.0.63"
quote = "1.0.26"
regex = "1.9.0"
syn = { version = "2.0.15", features = ["full"] }
57 changes: 1 addition & 56 deletions rearch-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::parse_macro_input;
use quote::{format_ident, quote};

/// Macro for handling some implementation boilerplate; do not use.
#[proc_macro]
Expand Down Expand Up @@ -42,57 +41,3 @@ pub fn generate_tuple_side_effect_impl(input: TokenStream) -> TokenStream {
};
effect_impl.into()
}

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

let fn_name = input.sig.ident.clone();
let fn_visibility = input.vis.clone();
let fn_ret_type = get_fn_return_type(&input);
let fn_registrar_parameter = get_fn_registrar_parameter(&input)
.map(|param| quote! { #param })
.unwrap_or(quote! { _: rearch::SideEffectRegistrar });
let fn_body: proc_macro2::TokenStream = {
let original = input.block.to_token_stream().to_string();
let re = regex::Regex::new(r"[^\w]_(\w+)").unwrap();
re.replace_all(&original, "__reader.read($1)")
.parse()
.unwrap()
};

let capsule_impl = quote! {
#fn_visibility fn #fn_name(
mut __reader: rearch::CapsuleReader,
#fn_registrar_parameter,
) -> #fn_ret_type {
#fn_body
}
};

capsule_impl.into()
}

fn get_fn_return_type(input: &syn::ItemFn) -> syn::Type {
match input.sig.output.clone() {
syn::ReturnType::Default => panic!("Capsules must return a static or owned type"),
syn::ReturnType::Type(_, t) => *t,
}
}

fn get_fn_registrar_parameter(func: &syn::ItemFn) -> Option<syn::PatType> {
let mut paths = func
.sig
.inputs
.clone()
.into_iter()
.map(move |param| match param {
syn::FnArg::Receiver(_) => panic!("Macro capsule functions must be top-level"),
syn::FnArg::Typed(pat) => pat,
});
let registrar_name = paths.next();
if registrar_name.is_some() && paths.next().is_some() {
panic!("Macro capsule functions may only consume 1 parameter, the SideEffectRegistrar");
}
registrar_name
}
37 changes: 17 additions & 20 deletions rearch/src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,38 @@
use rearch::{capsule, side_effects, CapsuleReader, Container, SideEffectRegistrar};
use rearch::{side_effects, CapsuleHandle, Container};

#[capsule]
fn count() -> i32 {
fn count(_: CapsuleHandle) -> i32 {
0
}

#[capsule]
fn count_plus_one() -> i32 {
_count + 1
fn count_plus_one(CapsuleHandle { mut get, .. }: CapsuleHandle) -> i32 {
get.get(count) + 1
}

fn crazy(mut reader: CapsuleReader, _: SideEffectRegistrar) -> &'static str {
reader.read(count);
reader.read(count_plus_one);
fn crazy(CapsuleHandle { mut get, .. }: CapsuleHandle) -> &'static str {
get.get(count);
get.get(count_plus_one);
"crazy!"
}

fn big_string_factory(
mut reader: CapsuleReader,
_: SideEffectRegistrar,
CapsuleHandle { mut get, .. }: CapsuleHandle,
) -> impl Fn(&str) -> String + Clone + Send + Sync {
let count = reader.read(count);
let count_plus_one = reader.read(count_plus_one);
let crazy = reader.read(crazy);
let count = get.get(count);
let count_plus_one = get.get(count_plus_one);
let crazy = get.get(crazy);
move |other| {
format!("param: {other}, count: {count}, count_plus_one: {count_plus_one}, crazy: {crazy}")
}
}

#[capsule]
fn uses_factory() -> String {
_big_string_factory("argument supplied to factory")
fn uses_factory(CapsuleHandle { mut get, .. }: CapsuleHandle) -> String {
get.get(big_string_factory)("argument supplied to factory")
}

#[capsule]
fn stateful(registrar: SideEffectRegistrar) -> (u32, impl Fn(u32) + Clone + Send + Sync) {
let (state, set_state) = registrar.register(side_effects::state(0));
fn stateful(
CapsuleHandle { register, .. }: CapsuleHandle,
) -> (u32, impl Fn(u32) + Clone + Send + Sync) {
let (state, set_state) = register.register(side_effects::state(0));
(*state, set_state)
}

Expand Down
2 changes: 1 addition & 1 deletion rearch/src/capsule_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<'scope, 'total> CapsuleReader<'scope, 'total> {
///
/// # Panics
/// Panics when a capsule attempts to read itself in its first build.
pub fn read<C: Capsule>(&mut self, capsule: C) -> C::Data {
pub fn get<C: Capsule>(&mut self, capsule: C) -> C::Data {
match self {
CapsuleReader::Normal { id, txn } => {
let (this, other) = (*id, TypeId::of::<C>());
Expand Down
Loading

0 comments on commit 5fbc2b5

Please sign in to comment.