use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::{
cell::RefCell,
collections::HashMap,
rc::Rc,
sync::atomic::{AtomicU64, Ordering},
};
use tokio::sync::mpsc::UnboundedSender;
pub struct App {
pub world: u64,
tx: UnboundedSender<crate::base::Message>,
events: RefCell<HashMap<String, Vec<(AppOnEvent, AppEventSource)>>>,
next_id: AtomicU64,
}
impl App {
pub fn on<T: for<'a> Deserialize<'a> + 'static>(
&self,
f: Box<dyn Fn(T) + 'static>,
source: AppEventSource,
) where
T: Default,
{
self.events
.borrow_mut()
.entry(std::any::type_name::<T>().to_string())
.or_default()
.push((
Box::new(move |v| {
let mut de = serde_cbor::de::Deserializer::from_slice(v);
if let Ok(event) = T::deserialize(&mut de) {
f(event)
}
}),
source,
))
}
pub fn emit(&self, t: &String, b: &Bytes) {
let binding = self.events.borrow();
let events = binding.get(t).clone();
if let Some(events) = events {
for (invoke, _) in events {
invoke(b);
}
}
}
pub fn off<T>(&self, source: AppEventSource) {
let t = std::any::type_name::<T>().to_string();
if let Some(vs) = self.events.borrow_mut().get_mut(&t) {
vs.retain(|(_, s)| *s != source);
}
}
pub fn invoke<T: Serialize>(&self, event: &T) {
if let Ok(v) = serde_cbor::to_vec(event) {
let _ = self
.tx
.send(crate::base::Message::Event(crate::base::Event::Invoke {
t: std::any::type_name::<T>().to_string(),
v: Bytes::from(v),
world: self.world,
}));
}
}
pub fn invoke_to_world<T: Serialize>(&self, event: &T, world: u64) {
if let Ok(v) = serde_cbor::to_vec(event) {
let _ = self
.tx
.send(crate::base::Message::Event(crate::base::Event::Invoke {
t: std::any::type_name::<T>().to_string(),
v: Bytes::from(v),
world,
}));
}
}
pub fn create(&self, skin: &crate::view::Skin) -> Rc<Page> {
let id = self.next_id.fetch_add(1, Ordering::SeqCst) + 1;
let p = Rc::new(Page { id: id });
let _ = self
.tx
.send(crate::base::Message::Event(crate::base::Event::Spawn {
id,
t: std::any::type_name::<Page>().to_string(),
world: self.world,
}));
let _ = self
.tx
.send(crate::base::Message::Event(crate::base::Event::Change {
id,
t: std::any::type_name::<crate::view::Skin>().to_string(),
v: Bytes::from(serde_cbor::to_vec(skin).unwrap()),
world: self.world,
}));
p
}
}
pub struct Page {
pub id: u64,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct PageEvent<T> {
pub id: u64,
pub event: T,
}
impl Page {
pub fn on<T: for<'a> Deserialize<'a> + 'static>(
&self,
app: Rc<App>,
f: Box<dyn Fn(T) + 'static>,
) where
T: Default,
{
let id = self.id;
app.on::<PageEvent<T>>(
Box::new(move |v| {
if v.id == id {
f(v.event);
}
}),
AppEventSource::Page(self.id),
);
}
pub fn off<T>(&self, app: Rc<App>) {
app.off::<PageEvent<T>>(AppEventSource::Page(self.id));
}
pub fn close(&self, app: Rc<App>) {
let _ = app
.tx
.send(crate::base::Message::Event(crate::base::Event::Despawn {
id: self.id,
world: app.world,
}));
}
pub fn set<T: Serialize>(&self, app: Rc<App>, data: &T) {
if let Ok(v) = serde_cbor::to_vec(data) {
let _ = app
.tx
.send(crate::base::Message::Event(crate::base::Event::Change {
id: self.id,
t: std::any::type_name::<T>().to_string(),
v: Bytes::from(v),
world: app.world,
}));
}
}
pub fn invoke<T: Serialize>(&self, app: Rc<App>, event: &T) {
app.invoke(&PageEvent {
id: self.id,
event: event,
});
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AppEventSource {
App,
Page(u64),
User(u64),
}
pub type AppOnEvent = Box<dyn Fn(&Bytes) + 'static>;
pub async fn run<F>(world: u64, f: fn(Rc<App>) -> F)
where
F: std::future::Future<Output = ()> + 'static,
{
let mut ch = crate::base::RUNTIME.chan();
let app = Rc::new(App {
world: world,
tx: ch.tx,
events: RefCell::new(HashMap::new()),
next_id: AtomicU64::new(0),
});
let run_task = tokio::task::spawn_local({
let app = app.clone();
let f = f;
async move {
f(app).await;
}
});
let recv_task = tokio::task::spawn_local({
let app = app.clone();
let world = world;
async move {
while let Some(e) = ch.rx.recv().await {
match e {
crate::base::Message::Event(event) => match event {
crate::base::Event::Invoke { t, v, world: w } => {
if w == world {
app.emit(&t, &v);
}
}
_ => {}
},
_ => {}
}
}
}
});
tokio::select! {
_ = run_task => {},
_ = recv_task => {},
}
}