Skip to content

Commit 206861f

Browse files
committed
redo hooks mechanism
1 parent 2d9fef6 commit 206861f

File tree

14 files changed

+230
-280
lines changed

14 files changed

+230
-280
lines changed

examples/counter.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,9 @@ struct CounterState {
66
count: Signal<i32>,
77
}
88

9-
#[hooks]
10-
struct CounterHooks {
11-
run_loop: UseAsync,
12-
}
13-
149
#[component]
15-
fn Counter(mut state: CounterState, hooks: &mut CounterHooks) -> impl Into<AnyElement<'static>> {
16-
hooks.run_loop.spawn_once(move || async move {
10+
fn Counter(mut state: CounterState, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
11+
hooks.use_future(async move {
1712
loop {
1813
smol::Timer::after(Duration::from_millis(100)).await;
1914
state.count += 1;

examples/form.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ struct FormContext<'a> {
4242
system: &'a mut SystemContext,
4343
}
4444

45-
#[hooks]
46-
struct FormHooks {
47-
input: UseInput,
48-
}
49-
5045
#[state]
5146
struct FormState {
5247
first_name: Signal<String>,
@@ -65,10 +60,10 @@ struct FormProps<'a> {
6560
fn Form<'a>(
6661
props: &mut FormProps<'a>,
6762
state: FormState,
68-
hooks: &mut FormHooks,
63+
mut hooks: Hooks,
6964
context: FormContext,
7065
) -> impl Into<AnyElement<'static>> {
71-
hooks.input.use_terminal_events(move |event| match event {
66+
hooks.use_terminal_events(move |event| match event {
7267
TerminalEvent::Key(KeyEvent { code, kind, .. }) if kind != KeyEventKind::Release => {
7368
match code {
7469
KeyCode::Enter => state.should_submit.set(true),

examples/progress_bar.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,13 @@ struct ProgressBarState {
1111
progress: Signal<f32>,
1212
}
1313

14-
#[hooks]
15-
struct ProgressBarHooks {
16-
run_loop: UseAsync,
17-
}
18-
1914
#[component]
2015
fn ProgressBar(
2116
state: ProgressBarState,
22-
hooks: &mut ProgressBarHooks,
17+
mut hooks: Hooks,
2318
context: ProgressBarContext,
2419
) -> impl Into<AnyElement<'static>> {
25-
hooks.run_loop.spawn_once(move || async move {
20+
hooks.use_future(async move {
2621
loop {
2722
smol::Timer::after(Duration::from_millis(100)).await;
2823
state.progress.set((state.progress.get() + 2.0).min(100.0));

examples/use_input.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ struct ExampleState {
1313
y: Signal<u32>,
1414
}
1515

16-
#[hooks]
17-
struct ExampleHooks {
18-
input: UseInput,
19-
}
20-
2116
const AREA_WIDTH: u32 = 80;
2217
const AREA_HEIGHT: u32 = 11;
2318
const FACE: &str = "👾";
@@ -26,9 +21,9 @@ const FACE: &str = "👾";
2621
fn Example(
2722
context: ExampleContext,
2823
state: ExampleState,
29-
hooks: &mut ExampleHooks,
24+
mut hooks: Hooks,
3025
) -> impl Into<AnyElement<'static>> {
31-
hooks.input.use_terminal_events({
26+
hooks.use_terminal_events({
3227
let x = state.x;
3328
let y = state.y;
3429
move |event| match event {

examples/use_output.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
use iocraft::prelude::*;
22
use std::time::Duration;
33

4-
#[hooks]
5-
struct ExampleHooks {
6-
run_loop: UseAsync,
7-
output: UseOutput,
8-
}
9-
104
#[component]
11-
fn Example(hooks: &mut ExampleHooks) -> impl Into<AnyElement> {
12-
let stdout = hooks.output.use_stdout();
13-
let stderr = hooks.output.use_stderr();
5+
fn Example(mut hooks: Hooks) -> impl Into<AnyElement> {
6+
let (stdout, stderr) = hooks.use_output();
147

15-
hooks.run_loop.spawn_once(|| async move {
8+
hooks.use_future(async move {
169
loop {
1710
smol::Timer::after(Duration::from_secs(1)).await;
1811
stdout.println("Hello from iocraft to stdout!");

packages/iocraft-macros/src/lib.rs

Lines changed: 27 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -314,91 +314,6 @@ pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
314314
quote!(#state).into()
315315
}
316316

317-
struct ParsedHooks {
318-
hooks: ItemStruct,
319-
}
320-
321-
impl Parse for ParsedHooks {
322-
fn parse(input: ParseStream) -> Result<Self> {
323-
let hooks: ItemStruct = input.parse()?;
324-
Ok(Self { hooks })
325-
}
326-
}
327-
328-
impl ToTokens for ParsedHooks {
329-
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
330-
let hooks = &self.hooks;
331-
let name = &hooks.ident;
332-
333-
let status_vars = hooks.fields.iter().map(|field| {
334-
let field_name = &field.ident;
335-
quote! { let #field_name = std::pin::Pin::new(&mut self.#field_name).poll_change(cx); }
336-
});
337-
let returns = hooks.fields.iter().map(|field| {
338-
let field_name = &field.ident;
339-
quote! {
340-
if #field_name.is_ready() {
341-
return std::task::Poll::Ready(());
342-
}
343-
}
344-
});
345-
let pre_component_updates = hooks.fields.iter().map(|field| {
346-
let field_name = &field.ident;
347-
quote! {
348-
self.#field_name.pre_component_update(updater);
349-
}
350-
});
351-
let post_component_updates = hooks.fields.iter().map(|field| {
352-
let field_name = &field.ident;
353-
quote! {
354-
self.#field_name.post_component_update(updater);
355-
}
356-
});
357-
let pre_component_draws = hooks.fields.iter().map(|field| {
358-
let field_name = &field.ident;
359-
quote! {
360-
self.#field_name.pre_component_draw(drawer);
361-
}
362-
});
363-
364-
tokens.extend(quote! {
365-
#[derive(Default)]
366-
#hooks
367-
368-
impl ::iocraft::Hook for #name {
369-
fn poll_change(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context) -> std::task::Poll<()> {
370-
use ::iocraft::Hook;
371-
#(#status_vars)*
372-
#(#returns)*
373-
std::task::Poll::Pending
374-
}
375-
376-
fn pre_component_update(&mut self, updater: &mut ::iocraft::ComponentUpdater) {
377-
use ::iocraft::Hook;
378-
#(#pre_component_updates)*
379-
}
380-
381-
fn post_component_update(&mut self, updater: &mut ::iocraft::ComponentUpdater) {
382-
use ::iocraft::Hook;
383-
#(#post_component_updates)*
384-
}
385-
386-
fn pre_component_draw(&mut self, drawer: &mut ::iocraft::ComponentDrawer) {
387-
use ::iocraft::Hook;
388-
#(#pre_component_draws)*
389-
}
390-
}
391-
});
392-
}
393-
}
394-
395-
/// Defines a struct containing hooks to be made available to components.
396-
#[proc_macro_attribute]
397-
pub fn hooks(_attr: TokenStream, item: TokenStream) -> TokenStream {
398-
let hooks = parse_macro_input!(item as ParsedHooks);
399-
quote!(#hooks).into()
400-
}
401-
402317
struct ParsedContext {
403318
context: ItemStruct,
404319
}
@@ -486,7 +401,6 @@ struct ParsedComponent {
486401
f: ItemFn,
487402
props_type: Option<Box<Type>>,
488403
state_type: Option<Box<Type>>,
489-
hooks_type: Option<Box<Type>>,
490404
context_type: Option<Box<Type>>,
491405
impl_args: Vec<proc_macro2::TokenStream>,
492406
}
@@ -497,7 +411,6 @@ impl Parse for ParsedComponent {
497411

498412
let mut props_type = None;
499413
let mut state_type = None;
500-
let mut hooks_type = None;
501414
let mut context_type = None;
502415
let mut impl_args = Vec::new();
503416

@@ -522,6 +435,15 @@ impl Parse for ParsedComponent {
522435
_ => return Err(Error::new(arg.ty.span(), "invalid `props` type")),
523436
}
524437
}
438+
"hooks" | "_hooks" => match &*arg.ty {
439+
Type::Reference(_) => {
440+
impl_args.push(quote!(&mut hooks));
441+
}
442+
Type::Path(_) => {
443+
impl_args.push(quote!(hooks));
444+
}
445+
_ => return Err(Error::new(arg.ty.span(), "invalid `hooks` type")),
446+
},
525447
"state" | "_state" => {
526448
if state_type.is_some() {
527449
return Err(Error::new(arg.span(), "duplicate `state` argument"));
@@ -538,18 +460,6 @@ impl Parse for ParsedComponent {
538460
_ => return Err(Error::new(arg.ty.span(), "invalid `state` type")),
539461
}
540462
}
541-
"hooks" | "_hooks" => {
542-
if hooks_type.is_some() {
543-
return Err(Error::new(arg.span(), "duplicate `hooks` argument"));
544-
}
545-
match &*arg.ty {
546-
Type::Reference(r) => {
547-
hooks_type = Some(r.elem.clone());
548-
impl_args.push(quote!(&mut self.hooks));
549-
}
550-
_ => return Err(Error::new(arg.ty.span(), "invalid `hooks` type")),
551-
}
552-
}
553463
"context" | "_context" => {
554464
if context_type.is_some() {
555465
return Err(Error::new(arg.span(), "duplicate `context` argument"));
@@ -578,7 +488,6 @@ impl Parse for ParsedComponent {
578488
f,
579489
props_type,
580490
state_type,
581-
hooks_type,
582491
context_type,
583492
impl_args,
584493
})
@@ -601,36 +510,6 @@ impl ToTokens for ParsedComponent {
601510
.as_ref()
602511
.map(|ty| quote!(state: #ty::new(&mut signal_owner),));
603512

604-
let hooks_decl = self.hooks_type.as_ref().map(|ty| quote!(hooks: #ty,));
605-
let hooks_init = self
606-
.hooks_type
607-
.as_ref()
608-
.map(|ty| quote!(hooks: #ty::default(),));
609-
let hooks_status_check = self.hooks_type.as_ref().map(|_| {
610-
quote! {
611-
let hooks_status = std::pin::Pin::new(&mut self.hooks).poll_change(cx);
612-
}
613-
});
614-
let hooks_status_return = self.hooks_type.as_ref().map(|_| {
615-
quote! {
616-
if hooks_status.is_ready() {
617-
return std::task::Poll::Ready(());
618-
}
619-
}
620-
});
621-
let hooks_pre_component_update = self
622-
.hooks_type
623-
.as_ref()
624-
.map(|_| quote! { self.hooks.pre_component_update(updater); });
625-
let hooks_post_component_update = self
626-
.hooks_type
627-
.as_ref()
628-
.map(|_| quote! { self.hooks.post_component_update(updater); });
629-
let hooks_pre_component_draw = self
630-
.hooks_type
631-
.as_ref()
632-
.map(|_| quote! { self.hooks.pre_component_draw(drawer); });
633-
634513
let props_type_name = self
635514
.props_type
636515
.as_ref()
@@ -646,8 +525,9 @@ impl ToTokens for ParsedComponent {
646525
tokens.extend(quote! {
647526
#vis struct #name {
648527
signal_owner: ::iocraft::SignalOwner,
528+
hooks: Vec<std::boxed::Box<dyn ::iocraft::AnyHook>>,
529+
first_update: bool,
649530
#state_decl
650-
#hooks_decl
651531
}
652532

653533
impl #name {
@@ -661,37 +541,43 @@ impl ToTokens for ParsedComponent {
661541
let mut signal_owner = ::iocraft::SignalOwner::new();
662542
Self {
663543
#state_init
664-
#hooks_init
544+
hooks: Vec::new(),
545+
first_update: true,
665546
signal_owner,
666547
}
667548
}
668549

669550
fn update(&mut self, props: &mut Self::Props<'_>, updater: &mut ::iocraft::ComponentUpdater) {
670-
#hooks_pre_component_update
551+
use ::iocraft::Hook;
552+
self.hooks.pre_component_update(updater);
671553
{
554+
let hooks = ::iocraft::Hooks::new(&mut self.hooks, self.first_update);
672555
let mut e = {
673556
#context_refs
674557
Self::implementation(#(#impl_args),*).into()
675558
};
676559
updater.update_children([&mut e], None);
677560
}
678-
#hooks_post_component_update
561+
self.hooks.post_component_update(updater);
562+
self.first_update = false;
679563
}
680564

681565
fn draw(&mut self, drawer: &mut ::iocraft::ComponentDrawer) {
682-
#hooks_pre_component_draw
566+
use ::iocraft::Hook;
567+
self.hooks.pre_component_draw(drawer);
683568
}
684569

685570
fn poll_change(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<()> {
571+
use ::iocraft::Hook;
572+
686573
let signals_status = std::pin::Pin::new(&mut self.signal_owner).poll_change(cx);
687-
#hooks_status_check
574+
let hooks_status = std::pin::Pin::new(&mut self.hooks).poll_change(cx);
688575

689-
if signals_status.is_ready() {
690-
return std::task::Poll::Ready(());
576+
if signals_status.is_ready() || hooks_status.is_ready() {
577+
std::task::Poll::Ready(())
578+
} else {
579+
std::task::Poll::Pending
691580
}
692-
#hooks_status_return
693-
694-
std::task::Poll::Pending
695581
}
696582
}
697583
});

packages/iocraft-macros/tests/hooks.rs

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)