use std::rc::Rc;
use serde::de::DeserializeOwned;
use crate::actor::event_bus::{Event, GlobalEventBus};
use crate::actor::short_type_name;
use crate::scope::Scope;
use crate::trace::{self, Point};
pub type Emit = fn(&Rc<Scope>, &str) -> Option<Result<u64, String>>;
pub struct RemoteAction {
pub name: &'static str,
pub path: &'static str,
pub answered_by: fn(&Rc<Scope>) -> bool,
pub emit: Emit,
}
pub struct RemoteEvent {
pub name: &'static str,
pub path: &'static str,
pub publish: fn(&str) -> Result<u64, String>,
}
inventory::collect!(RemoteAction);
inventory::collect!(RemoteEvent);
pub fn act_in(scopes: &[Rc<Scope>], named: &str, json: &str) -> Result<u64, String> {
let candidates: Vec<&RemoteAction> = inventory::iter::<RemoteAction>()
.filter(|action| action.name == named || action.path == named)
.collect();
if candidates.is_empty() {
return Err(format!(
"no action is registered as {named:?} - these are: {:?}",
actions()
));
}
for scope in scopes.iter().rev() {
let answering: Vec<&RemoteAction> = candidates
.iter()
.copied()
.filter(|action| (action.answered_by)(scope))
.collect();
match answering.as_slice() {
[] => continue,
[one] => {
return (one.emit)(scope, json)
.unwrap_or_else(|| Err(format!("{} stopped answering", one.path)));
}
several => {
let paths: Vec<&str> = several.iter().map(|action| action.path).collect();
return Err(format!(
"{} actions called {named:?} are answered here - send one by its path: {paths:?}",
several.len()
));
}
}
}
Err(format!("nothing on the open page answers {named}"))
}
pub fn publish(named: &str, json: &str) -> Result<u64, String> {
let candidates: Vec<&RemoteEvent> = inventory::iter::<RemoteEvent>()
.filter(|event| event.name == named || event.path == named)
.collect();
match candidates.as_slice() {
[] => Err(format!(
"no event is registered as {named:?} - these are: {:?}",
events()
)),
[one] => (one.publish)(json),
several => {
let paths: Vec<&str> = several.iter().map(|event| event.path).collect();
Err(format!(
"{} events are called {named:?} - send one by its path: {paths:?}",
several.len()
))
}
}
}
pub fn actions() -> Vec<&'static str> {
let mut names: Vec<&'static str> = inventory::iter::<RemoteAction>()
.map(|action| action.name)
.collect();
names.sort_unstable();
names.dedup();
names
}
pub fn events() -> Vec<&'static str> {
let mut names: Vec<&'static str> = inventory::iter::<RemoteEvent>()
.map(|event| event.name)
.collect();
names.sort_unstable();
names.dedup();
names
}
pub fn answered_by<M: 'static>(scope: &Rc<Scope>) -> bool {
scope.first_answerer::<M>().is_some()
}
pub fn emit_json<M: DeserializeOwned + 'static>(
scope: &Rc<Scope>,
json: &str,
) -> Option<Result<u64, String>> {
let answer = scope.first_answerer::<M>()?;
let action: M = match serde_json::from_str(json) {
Ok(action) => action,
Err(error) => return Some(Err(format!("{}: {error}", short_type_name::<M>()))),
};
let entered = trace::enter(|| Point::Action {
message: short_type_name::<M>(),
});
let cause = entered.id().get();
answer(action);
Some(Ok(cause))
}
pub fn publish_json<M: Event + DeserializeOwned>(json: &str) -> Result<u64, String> {
let event: M = serde_json::from_str(json)
.map_err(|error| format!("{}: {error}", short_type_name::<M>()))?;
let entered = trace::enter(|| Point::Action {
message: short_type_name::<M>(),
});
let cause = entered.id().get();
GlobalEventBus::bus().publish(event);
Ok(cause)
}