Skip to content

Commit

Permalink
use_context
Browse files Browse the repository at this point in the history
  • Loading branch information
ccbrown committed Sep 20, 2024
1 parent 27ad5ef commit b16368c
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 263 deletions.
11 changes: 4 additions & 7 deletions examples/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ use iocraft::prelude::*;

struct NumberOfTheDay(i32);

#[context]
struct MyContextConsumerContext<'a> {
number: &'a NumberOfTheDay,
}

#[component]
fn MyContextConsumer(context: MyContextConsumerContext) -> impl Into<AnyElement<'static>> {
fn MyContextConsumer(hooks: Hooks) -> impl Into<AnyElement<'static>> {
let number = hooks.use_context::<NumberOfTheDay>();

element! {
Box(border_style: BorderStyle::Round, border_color: Color::Cyan) {
Text(content: "The number of the day is... ")
Text(color: Color::Green, weight: Weight::Bold, content: context.number.0.to_string())
Text(color: Color::Green, weight: Weight::Bold, content: number.0.to_string())
Text(content: "!")
}
}
Expand Down
15 changes: 4 additions & 11 deletions examples/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,16 @@ fn FormField(props: &FormFieldProps) -> impl Into<AnyElement<'static>> {
}
}

#[context]
struct FormContext<'a> {
system: &'a mut SystemContext,
}

#[props]
struct FormProps<'a> {
first_name_out: Option<&'a mut String>,
last_name_out: Option<&'a mut String>,
}

#[component]
fn Form<'a>(
props: &mut FormProps<'a>,
mut hooks: Hooks,
context: FormContext,
) -> impl Into<AnyElement<'static>> {
fn Form<'a>(props: &mut FormProps<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut system = hooks.use_context_mut::<SystemContext>();

let first_name = hooks.use_state(|| "".to_string());
let last_name = hooks.use_state(|| "".to_string());
let focus = hooks.use_state(|| 0);
Expand All @@ -77,7 +70,7 @@ fn Form<'a>(
if let Some(last_name_out) = props.last_name_out.as_mut() {
**last_name_out = last_name.to_string();
}
context.system.exit();
system.exit();
element!(Box)
} else {
element! {
Expand Down
10 changes: 3 additions & 7 deletions examples/progress_bar.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use iocraft::prelude::*;
use std::time::Duration;

#[context]
struct ProgressBarContext<'a> {
system: &'a mut SystemContext,
}

#[component]
fn ProgressBar(mut hooks: Hooks, context: ProgressBarContext) -> impl Into<AnyElement<'static>> {
fn ProgressBar(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut system = hooks.use_context_mut::<SystemContext>();
let progress = hooks.use_state::<f32, _>(|| 0.0);

hooks.use_future(async move {
Expand All @@ -18,7 +14,7 @@ fn ProgressBar(mut hooks: Hooks, context: ProgressBarContext) -> impl Into<AnyEl
});

if progress >= 100.0 {
context.system.exit();
system.exit();
}

element! {
Expand Down
10 changes: 3 additions & 7 deletions examples/use_input.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use iocraft::prelude::*;
use unicode_width::UnicodeWidthStr;

#[context]
struct ExampleContext<'a> {
system: &'a mut SystemContext,
}

const AREA_WIDTH: u32 = 80;
const AREA_HEIGHT: u32 = 11;
const FACE: &str = "👾";

#[component]
fn Example(context: ExampleContext, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
fn Example(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let mut system = hooks.use_context_mut::<SystemContext>();
let x = hooks.use_state(|| 0);
let y = hooks.use_state(|| 0);
let should_exit = hooks.use_state(|| false);
Expand All @@ -33,7 +29,7 @@ fn Example(context: ExampleContext, mut hooks: Hooks) -> impl Into<AnyElement<'s
});

if should_exit.get() {
context.system.exit();
system.exit();
}

element! {
Expand Down
2 changes: 1 addition & 1 deletion examples/use_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use iocraft::prelude::*;
use std::time::Duration;

#[component]
fn Example(mut hooks: Hooks) -> impl Into<AnyElement> {
fn Example(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
let (stdout, stderr) = hooks.use_output();

hooks.use_future(async move {
Expand Down
113 changes: 2 additions & 111 deletions packages/iocraft-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,93 +272,9 @@ pub fn props(_attr: TokenStream, item: TokenStream) -> TokenStream {
quote!(#props).into()
}

struct ParsedContext {
context: ItemStruct,
}

impl Parse for ParsedContext {
fn parse(input: ParseStream) -> Result<Self> {
let context: ItemStruct = input.parse()?;
Ok(Self { context })
}
}

impl ToTokens for ParsedContext {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let context = &self.context;
let name = &context.ident;
let generics = &context.generics;
let lifetime = generics.params.first();

let ref_fields = context.fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
quote! { #field_name: <#field_type as ::iocraft::ContextRef<#lifetime>>::RefOwner<#lifetime> }
});

let ref_field_assignments = context.fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
quote! { #field_name: <#field_type as ::iocraft::ContextRef>::get_from_component_updater(updater) }
});

let ref_field_borrows = context.fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
quote! { #field_name: <#field_type as ::iocraft::ContextRef>::borrow(&mut refs.#field_name) }
});

tokens.extend(quote! {
#context

const _: () = {
pub struct ContextRefs #generics {
#(#ref_fields,)*
}

impl<'iocraft_lta> ContextRefs<'iocraft_lta> {
fn refs_from_component_updater<#lifetime: 'iocraft_lta>(updater: &#lifetime ::iocraft::ComponentUpdater) -> ContextRefs<#lifetime> {
ContextRefs {
#(#ref_field_assignments,)*
}
}
}

impl<#lifetime> ContextRefs #generics {
fn borrow_refs<'iocraft_ltb: #lifetime, 'iocraft_ltc: 'iocraft_ltb>(refs: &'iocraft_ltb mut ContextRefs<'iocraft_ltc>) -> #name<#lifetime> {
#name {
#(#ref_field_borrows,)*
}
}
}

impl<'a> ::iocraft::ContextImplExt<'a> for #name<'a> {
type Refs<'b: 'a> = ContextRefs<'b>;

fn refs_from_component_updater<'b: 'a>(updater: &'b ::iocraft::ComponentUpdater) -> Self::Refs<'b> {
ContextRefs::refs_from_component_updater(updater)
}

fn borrow_refs<'b: 'a, 'c: 'b>(refs: &'b mut Self::Refs<'c>) -> Self {
ContextRefs::borrow_refs(refs)
}
}
};
});
}
}

/// Defines a struct containing context references to be made available to components.
#[proc_macro_attribute]
pub fn context(_attr: TokenStream, item: TokenStream) -> TokenStream {
let context = parse_macro_input!(item as ParsedContext);
quote!(#context).into()
}

struct ParsedComponent {
f: ItemFn,
props_type: Option<Box<Type>>,
context_type: Option<Box<Type>>,
impl_args: Vec<proc_macro2::TokenStream>,
}

Expand All @@ -367,7 +283,6 @@ impl Parse for ParsedComponent {
let f: ItemFn = input.parse()?;

let mut props_type = None;
let mut context_type = None;
let mut impl_args = Vec::new();

for arg in &f.sig.inputs {
Expand Down Expand Up @@ -400,23 +315,6 @@ impl Parse for ParsedComponent {
}
_ => return Err(Error::new(arg.ty.span(), "invalid `hooks` type")),
},
"context" | "_context" => {
if context_type.is_some() {
return Err(Error::new(arg.span(), "duplicate `context` argument"));
}
match &*arg.ty {
Type::Path(_) => {
context_type = Some(arg.ty.clone());
impl_args.push({
let type_name = &arg.ty;
quote!(#type_name::borrow_refs(&mut context_refs))
});
}
_ => {
return Err(Error::new(arg.ty.span(), "invalid `context` type"))
}
}
}
_ => return Err(Error::new(arg.span(), "invalid argument")),
}
}
Expand All @@ -427,7 +325,6 @@ impl Parse for ParsedComponent {
Ok(Self {
f,
props_type,
context_type,
impl_args,
})
}
Expand All @@ -449,12 +346,6 @@ impl ToTokens for ParsedComponent {
.map(|ty| quote!(#ty))
.unwrap_or_else(|| quote!(::iocraft::NoProps));

let context_refs = self.context_type.as_ref().map(|ty| {
quote! {
let mut context_refs = #ty::refs_from_component_updater(updater);
}
});

tokens.extend(quote! {
#vis struct #name;

Expand All @@ -469,9 +360,9 @@ impl ToTokens for ParsedComponent {
Self
}

fn update(&mut self, props: &mut Self::Props<'_>, hooks: ::iocraft::Hooks, updater: &mut ::iocraft::ComponentUpdater) {
fn update(&mut self, props: &mut Self::Props<'_>, mut hooks: ::iocraft::Hooks, updater: &mut ::iocraft::ComponentUpdater) {
let mut e = {
#context_refs
let hooks = hooks.with_context_stack(updater.component_context_stack());
Self::implementation(#(#impl_args),*).into()
};
updater.update_children([&mut e], None);
Expand Down
12 changes: 0 additions & 12 deletions packages/iocraft-macros/tests/context.rs

This file was deleted.

13 changes: 3 additions & 10 deletions packages/iocraft/src/components/context_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,12 @@ mod tests {
use crate::prelude::*;

struct StringContext(String);
struct OtherContext;

#[context]
struct MyComponentContext<'a> {
string: &'a StringContext,
_other: Option<&'a OtherContext>,
_system: &'a mut SystemContext,
}

#[component]
fn MyComponent(context: MyComponentContext) -> impl Into<AnyElement<'static>> {
fn MyComponent(hooks: Hooks) -> impl Into<AnyElement<'static>> {
let s = hooks.use_context::<StringContext>();
element! {
Text(content: &context.string.0)
Text(content: &s.0)
}
}

Expand Down
Loading

0 comments on commit b16368c

Please sign in to comment.