coaxial/
context.rs

1use axum::response::Response;
2use generational_box::{AnyStorage, Owner, SyncStorage};
3use std::{collections::HashMap, future::Future, sync::Arc};
4use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
5
6use crate::{
7    closure::{Closure, ClosureTrait, ClosureWrapper, IntoClosure},
8    event_handlers::{EventHandler, EventHandlerWrapper},
9    html::Element,
10    state::{State, StateInner},
11    CoaxialResponse, Output,
12};
13
14pub struct Context<S = ()> {
15    uuid: u64,
16    index: u64,
17
18    state_owner: Owner<SyncStorage>,
19    pub(crate) closures: HashMap<String, Arc<dyn ClosureTrait<S>>>,
20    pub(crate) event_handlers: HashMap<String, Arc<dyn EventHandler>>,
21
22    pub(crate) changes_rx: UnboundedReceiver<(u64, String)>,
23    changes_tx: UnboundedSender<(u64, String)>,
24}
25
26impl<S> Context<S> {
27    pub fn use_closure<P, I>(&mut self, closure: I) -> Closure
28    where
29        I: IntoClosure<P, S> + Send + Sync + 'static,
30        P: Send + Sync + 'static,
31        ClosureWrapper<I, P>: ClosureTrait<S>,
32    {
33        self.index += 1;
34        let id = format!("{}-{}", self.uuid, self.index);
35        let closure: ClosureWrapper<I, P> = <I as IntoClosure<P, S>>::wrap(closure);
36        self.closures.insert(id.clone(), Arc::new(closure));
37
38        Closure { id }
39    }
40
41    #[track_caller]
42    pub fn use_state<T: Send + Sync>(&mut self, value: T) -> State<T> {
43        self.index += 1;
44        State {
45            inner: self.state_owner.insert_with_caller(
46                StateInner {
47                    value,
48                    changes_tx: self.changes_tx.clone(),
49                },
50                #[cfg(any(debug_assertions, feature = "debug_ownership"))]
51                std::panic::Location::caller(),
52            ),
53            id: self.index + self.uuid,
54        }
55    }
56
57    // TODO ideally, we would store a function that takes a type that impls Deserialize
58    // idk how to do it with multiple functions tho
59    pub fn on<F, Fut, P>(&mut self, name: impl ToString, closure: F)
60    where
61        F: Fn(P) -> Fut + Send + Sync + 'static,
62        Fut: Future<Output = ()> + Send + Sync + 'static,
63        P: serde::de::DeserializeOwned + Send + Sync + 'static,
64    {
65        self.event_handlers.insert(
66            name.to_string(),
67            Arc::new(EventHandlerWrapper::new(closure)),
68        );
69    }
70
71    pub fn with(self, element: Element) -> CoaxialResponse<S> {
72        Response::new(Output {
73            element,
74            context: self,
75        })
76    }
77}
78
79impl<S> Default for Context<S> {
80    fn default() -> Self {
81        let (changes_tx, changes_rx) = unbounded_channel();
82
83        Self {
84            // TODO generate something random
85            uuid: 100000,
86            index: 0,
87            state_owner: <SyncStorage as AnyStorage>::owner(),
88            closures: Default::default(),
89            event_handlers: Default::default(),
90
91            changes_rx,
92            changes_tx,
93        }
94    }
95}