Skip to main content

rate_ui/agents/live/
wire.rs

1use crate::agents::live::{LiveAgent, LiveRequest, LiveResponse, LiveStatus};
2use rill_protocol::flow::core;
3use rill_protocol::io::client::{ClientReqId, ClientRequest, ClientResponse};
4use rill_protocol::io::provider::{FlowControl, Path, RecorderAction, RecorderRequest};
5use std::fmt;
6use yew::worker::{AgentLink, HandlerId};
7
8#[derive(Debug)]
9pub struct WireEnvelope<ID, T> {
10    pub id: ID,
11    pub data: T,
12}
13
14impl<ID, T> WireEnvelope<ID, T> {
15    pub fn new(id: ID, data: T) -> Self {
16        Self { id, data }
17    }
18}
19
20// TODO: Don't require Debug
21pub trait WireTask: fmt::Debug + 'static {
22    fn on_action(&mut self, action: WireAction, context: WireContext<'_>);
23}
24
25pub enum WireAction {
26    // It's separate `LiveStatus` type to make status cloneable
27    Status(LiveStatus),
28    Incoming(ClientResponse),
29    Interrupted,
30}
31
32pub struct WireContext<'a> {
33    pub who: HandlerId,
34    pub req_id: ClientReqId,
35    pub link: &'a mut AgentLink<LiveAgent>,
36}
37
38impl<'a> WireContext<'a> {
39    fn send_to_server(&mut self, request: ClientRequest) {
40        let input = LiveRequest::Forward(request);
41        let envelope = WireEnvelope::new(self.req_id, input);
42        self.link.send_input(envelope);
43    }
44
45    fn send_to_component(&mut self, response: ClientResponse) {
46        let output = LiveResponse::Forwarded(response);
47        let envelope = WireEnvelope::new(self.req_id, output);
48        self.link.respond(self.who, envelope);
49    }
50
51    fn shutdown(&mut self) {
52        let input = LiveRequest::DetachRuntime;
53        let envelope = WireEnvelope::new(self.req_id, input);
54        self.link.send_input(envelope);
55    }
56}
57
58#[derive(Debug)]
59pub struct Subscription {
60    path: Path,
61    sent: bool,
62    interrupted: bool,
63}
64
65impl Subscription {
66    pub fn new(path: Path) -> Self {
67        Self {
68            path,
69            sent: false,
70            interrupted: false,
71        }
72    }
73}
74
75impl WireTask for Subscription {
76    fn on_action(&mut self, action: WireAction, mut ctx: WireContext<'_>) {
77        match action {
78            WireAction::Status(LiveStatus::Connected) => {}
79            WireAction::Status(LiveStatus::AccessLevel(_)) => {
80                if !self.sent {
81                    self.sent = true;
82                    if !self.interrupted {
83                        let control = FlowControl::StartStream;
84                        let request = RecorderRequest::ControlStream(control);
85                        let request = ClientRequest {
86                            path: self.path.clone(),
87                            request,
88                        };
89                        ctx.send_to_server(request);
90                    }
91                }
92            }
93            WireAction::Status(LiveStatus::Disconnected) => {
94                self.sent = false;
95                // TODO: Send `Disconnected` to a Component
96                //ctx.shutdown();
97            }
98            WireAction::Incoming(response) => {
99                if !self.interrupted {
100                    match &response {
101                        ClientResponse::Done => {
102                            self.interrupted = true;
103                        }
104                        ClientResponse::Error(err) => {
105                            log::error!("Stream {} failed: {}", self.path, err);
106                        }
107                        _ => {}
108                    }
109                    // TODO: Try again if had an error: for the case if the server was restarted
110                    // and not all providers sent when the client tried to reach them.
111                    //
112                    ctx.send_to_component(response);
113                }
114            }
115            WireAction::Interrupted => {
116                if self.sent && !self.interrupted {
117                    self.interrupted = true;
118                    let control = FlowControl::StopStream;
119                    let request = RecorderRequest::ControlStream(control);
120                    let request = ClientRequest {
121                        path: self.path.clone(),
122                        request,
123                    };
124                    ctx.send_to_server(request);
125                    // Wait for the `End` marker or `Error` or `Disconnected`
126                }
127            }
128        }
129    }
130}
131
132#[derive(Debug)]
133pub struct GetDescription {
134    sent: bool,
135    path: Path,
136}
137
138impl GetDescription {
139    pub fn new(path: Path) -> Self {
140        Self { sent: false, path }
141    }
142}
143
144impl WireTask for GetDescription {
145    fn on_action(&mut self, action: WireAction, mut ctx: WireContext<'_>) {
146        match action {
147            WireAction::Status(LiveStatus::Connected) => {}
148            WireAction::Status(LiveStatus::AccessLevel(_)) => {
149                if !self.sent {
150                    self.sent = true;
151                    let action = RecorderAction::GetFlow;
152                    let request = RecorderRequest::Action(action);
153                    let request = ClientRequest {
154                        path: self.path.clone(),
155                        request,
156                    };
157                    ctx.send_to_server(request);
158                }
159            }
160            WireAction::Status(LiveStatus::Disconnected) => {
161                self.sent = false;
162                // TODO: Send End to Component
163                ctx.shutdown();
164            }
165            WireAction::Incoming(response) => {
166                // TODO: Match `End`, `Error, `Disconected`
167                ctx.send_to_component(response);
168                ctx.shutdown();
169            }
170            WireAction::Interrupted => {
171                // TODO: Set `drop_response/not_active` flag
172                // Wait for the `End` marker or `Error` or `Disconnected`
173                ctx.shutdown();
174            }
175        }
176    }
177}
178
179#[derive(Debug)]
180pub struct DoAction<T: core::Flow> {
181    sent: bool,
182    path: Path,
183    action: T::Action,
184}
185
186impl<T: core::Flow> DoAction<T> {
187    pub fn new(path: Path, action: T::Action) -> Self {
188        Self {
189            sent: false,
190            path,
191            action,
192        }
193    }
194}
195
196impl<T: core::Flow> WireTask for DoAction<T> {
197    fn on_action(&mut self, action: WireAction, mut ctx: WireContext<'_>) {
198        match action {
199            WireAction::Status(LiveStatus::Connected) => {}
200            WireAction::Status(LiveStatus::AccessLevel(_)) => {
201                if !self.sent {
202                    self.sent = true;
203                    match T::pack_action(&self.action) {
204                        Ok(packed_action) => {
205                            let action = RecorderAction::DoAction(packed_action);
206                            let request = RecorderRequest::Action(action);
207                            let request = ClientRequest {
208                                path: self.path.clone(),
209                                request,
210                            };
211                            ctx.send_to_server(request);
212                        }
213                        Err(err) => {
214                            log::error!("Can't pack an action: {}", err);
215                        }
216                    }
217                    ctx.shutdown();
218                }
219            }
220            WireAction::Status(LiveStatus::Disconnected) => {
221                // TODO: Send End to Component
222                ctx.shutdown();
223            }
224            WireAction::Incoming(_response) => {
225                // TODO: Match `End`, `Error, `Disconected`
226                ctx.shutdown();
227            }
228            WireAction::Interrupted => {
229                // TODO: Set `drop_response/not_active` flag
230                // Wait for the `End` marker or `Error` or `Disconnected`
231                ctx.shutdown();
232            }
233        }
234    }
235}