yew_stdweb/agent/
link.rs

1use super::*;
2use crate::callback::Callback;
3use crate::html::ImplicitClone;
4use crate::scheduler::{scheduler, Runnable, Shared};
5use std::cell::RefCell;
6use std::fmt;
7use std::rc::Rc;
8
9/// Defines communication from Worker to Consumers
10pub(crate) trait Responder<AGN: Agent> {
11    /// Implementation for communication channel from Worker to Consumers
12    fn respond(&self, id: HandlerId, output: AGN::Output);
13}
14
15/// Link to agent's scope for creating callbacks.
16pub struct AgentLink<AGN: Agent> {
17    scope: AgentScope<AGN>,
18    responder: Rc<dyn Responder<AGN>>,
19}
20
21impl<AGN: Agent> AgentLink<AGN> {
22    /// Create link for a scope.
23    pub(crate) fn connect<T>(scope: &AgentScope<AGN>, responder: T) -> Self
24    where
25        T: Responder<AGN> + 'static,
26    {
27        AgentLink {
28            scope: scope.clone(),
29            responder: Rc::new(responder),
30        }
31    }
32
33    /// Send response to an agent.
34    pub fn respond(&self, id: HandlerId, output: AGN::Output) {
35        self.responder.respond(id, output);
36    }
37
38    /// Send a message to the agent
39    pub fn send_message<T>(&self, msg: T)
40    where
41        T: Into<AGN::Message>,
42    {
43        self.scope.send(AgentLifecycleEvent::Message(msg.into()));
44    }
45
46    /// Send an input to self
47    pub fn send_input<T>(&self, input: T)
48    where
49        T: Into<AGN::Input>,
50    {
51        let handler_id = HandlerId::new(0, false);
52        self.scope
53            .send(AgentLifecycleEvent::Input(input.into(), handler_id));
54    }
55
56    /// Create a callback which will send a message to the agent when invoked.
57    pub fn callback<F, IN, M>(&self, function: F) -> Callback<IN>
58    where
59        M: Into<AGN::Message>,
60        F: Fn(IN) -> M + 'static,
61    {
62        let scope = self.scope.clone();
63        let closure = move |input| {
64            let output = function(input).into();
65            scope.send(AgentLifecycleEvent::Message(output));
66        };
67        closure.into()
68    }
69}
70
71impl<AGN: Agent> fmt::Debug for AgentLink<AGN> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.write_str("AgentLink<_>")
74    }
75}
76
77impl<AGN: Agent> Clone for AgentLink<AGN> {
78    fn clone(&self) -> Self {
79        AgentLink {
80            scope: self.scope.clone(),
81            responder: self.responder.clone(),
82        }
83    }
84}
85/// This struct holds a reference to a component and to a global scheduler.
86pub(crate) struct AgentScope<AGN: Agent> {
87    state: Shared<AgentState<AGN>>,
88}
89
90impl<AGN: Agent> fmt::Debug for AgentScope<AGN> {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.write_str("AgentScope<_>")
93    }
94}
95
96impl<AGN: Agent> Clone for AgentScope<AGN> {
97    fn clone(&self) -> Self {
98        AgentScope {
99            state: self.state.clone(),
100        }
101    }
102}
103
104impl<AGN: Agent> AgentScope<AGN> {
105    /// Create agent scope
106    pub fn new() -> Self {
107        let state = Rc::new(RefCell::new(AgentState::new()));
108        AgentScope { state }
109    }
110
111    /// Schedule message for sending to agent
112    pub fn send(&self, event: AgentLifecycleEvent<AGN>) {
113        let runnable: Box<dyn Runnable> = Box::new(AgentRunnable {
114            state: self.state.clone(),
115            event,
116        });
117        scheduler().push(runnable);
118    }
119}
120
121impl<AGN: Agent> Default for AgentScope<AGN> {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl<AGN: Agent> ImplicitClone for AgentScope<AGN> {}
128
129struct AgentState<AGN> {
130    agent: Option<AGN>,
131    // TODO(#939): Use agent field to control create message this flag
132    destroyed: bool,
133}
134
135impl<AGN> AgentState<AGN> {
136    fn new() -> Self {
137        AgentState {
138            agent: None,
139            destroyed: false,
140        }
141    }
142}
143
144/// Internal Agent lifecycle events
145#[derive(Debug)]
146pub(crate) enum AgentLifecycleEvent<AGN: Agent> {
147    /// Request to create link
148    Create(AgentLink<AGN>),
149    /// Internal Agent message
150    Message(AGN::Message),
151    /// Client connected
152    Connected(HandlerId),
153    /// Received message from Client
154    Input(AGN::Input, HandlerId),
155    /// Client disconnected
156    Disconnected(HandlerId),
157    /// Request to destroy agent
158    Destroy,
159}
160
161struct AgentRunnable<AGN: Agent> {
162    state: Shared<AgentState<AGN>>,
163    event: AgentLifecycleEvent<AGN>,
164}
165
166impl<AGN> Runnable for AgentRunnable<AGN>
167where
168    AGN: Agent,
169{
170    fn run(self: Box<Self>) {
171        let mut state = self.state.borrow_mut();
172        if state.destroyed {
173            return;
174        }
175        match self.event {
176            AgentLifecycleEvent::Create(link) => {
177                state.agent = Some(AGN::create(link));
178            }
179            AgentLifecycleEvent::Message(msg) => {
180                state
181                    .agent
182                    .as_mut()
183                    .expect("agent was not created to process messages")
184                    .update(msg);
185            }
186            AgentLifecycleEvent::Connected(id) => {
187                state
188                    .agent
189                    .as_mut()
190                    .expect("agent was not created to send a connected message")
191                    .connected(id);
192            }
193            AgentLifecycleEvent::Input(inp, id) => {
194                state
195                    .agent
196                    .as_mut()
197                    .expect("agent was not created to process inputs")
198                    .handle_input(inp, id);
199            }
200            AgentLifecycleEvent::Disconnected(id) => {
201                state
202                    .agent
203                    .as_mut()
204                    .expect("agent was not created to send a disconnected message")
205                    .disconnected(id);
206            }
207            AgentLifecycleEvent::Destroy => {
208                let mut agent = state
209                    .agent
210                    .take()
211                    .expect("trying to destroy not existent agent");
212                agent.destroy();
213            }
214        }
215    }
216}