1use serde::de::DeserializeOwned;
21
22use crate::actor::event_bus::{Event, GlobalEventBus};
23use crate::actor::short_type_name;
24use crate::scope::Scope;
25use crate::trace::{self, Point};
26
27pub type Emit = fn(Scope, &str) -> Option<Result<u64, String>>;
30
31pub struct RemoteAction {
33 pub name: &'static str,
35 pub path: &'static str,
37 pub answered_by: fn(Scope) -> bool,
39 pub emit: Emit,
40}
41
42pub struct RemoteEvent {
44 pub name: &'static str,
45 pub path: &'static str,
46 pub publish: fn(&str) -> Result<u64, String>,
47}
48
49inventory::collect!(RemoteAction);
50inventory::collect!(RemoteEvent);
51
52pub fn act_in(scopes: &[Scope], named: &str, json: &str) -> Result<u64, String> {
57 let candidates: Vec<&RemoteAction> = inventory::iter::<RemoteAction>()
58 .filter(|action| action.name == named || action.path == named)
59 .collect();
60
61 if candidates.is_empty() {
62 return Err(format!(
63 "no action is registered as {named:?} - these are: {:?}",
64 actions()
65 ));
66 }
67
68 for &scope in scopes.iter().rev() {
69 let answering: Vec<&RemoteAction> = candidates
70 .iter()
71 .copied()
72 .filter(|action| (action.answered_by)(scope))
73 .collect();
74
75 match answering.as_slice() {
76 [] => continue,
77 [one] => {
78 return (one.emit)(scope, json)
79 .unwrap_or_else(|| Err(format!("{} stopped answering", one.path)));
80 }
81 several => {
82 let paths: Vec<&str> = several.iter().map(|action| action.path).collect();
83 return Err(format!(
84 "{} actions called {named:?} are answered here - send one by its path: {paths:?}",
85 several.len()
86 ));
87 }
88 }
89 }
90
91 Err(format!("nothing on the open page answers {named}"))
92}
93
94pub fn publish(named: &str, json: &str) -> Result<u64, String> {
97 let candidates: Vec<&RemoteEvent> = inventory::iter::<RemoteEvent>()
98 .filter(|event| event.name == named || event.path == named)
99 .collect();
100
101 match candidates.as_slice() {
102 [] => Err(format!(
103 "no event is registered as {named:?} - these are: {:?}",
104 events()
105 )),
106 [one] => (one.publish)(json),
107 several => {
108 let paths: Vec<&str> = several.iter().map(|event| event.path).collect();
109 Err(format!(
110 "{} events are called {named:?} - send one by its path: {paths:?}",
111 several.len()
112 ))
113 }
114 }
115}
116
117pub fn actions() -> Vec<&'static str> {
119 let mut names: Vec<&'static str> = inventory::iter::<RemoteAction>()
120 .map(|action| action.name)
121 .collect();
122 names.sort_unstable();
123 names.dedup();
124 names
125}
126
127pub fn events() -> Vec<&'static str> {
129 let mut names: Vec<&'static str> = inventory::iter::<RemoteEvent>()
130 .map(|event| event.name)
131 .collect();
132 names.sort_unstable();
133 names.dedup();
134 names
135}
136
137pub fn answered_by<M: 'static>(scope: Scope) -> bool {
139 scope.first_answerer::<M>().is_some()
140}
141
142pub fn emit_json<M: DeserializeOwned + 'static>(
144 scope: Scope,
145 json: &str,
146) -> Option<Result<u64, String>> {
147 let answer = scope.first_answerer::<M>()?;
148
149 let action: M = match serde_json::from_str(json) {
150 Ok(action) => action,
151 Err(error) => return Some(Err(format!("{}: {error}", short_type_name::<M>()))),
152 };
153
154 let entered = trace::enter(|| Point::Action {
155 message: short_type_name::<M>(),
156 });
157 let cause = entered.id().get();
158 answer(action);
159
160 Some(Ok(cause))
161}
162
163pub fn publish_json<M: Event + DeserializeOwned>(json: &str) -> Result<u64, String> {
166 let event: M = serde_json::from_str(json)
167 .map_err(|error| format!("{}: {error}", short_type_name::<M>()))?;
168
169 let entered = trace::enter(|| Point::Action {
170 message: short_type_name::<M>(),
171 });
172 let cause = entered.id().get();
173 GlobalEventBus::bus().publish(event);
174
175 Ok(cause)
176}