1use crate::client::Client;
2use crate::protocol::{Client as InnerClient, IntoStaticFuture, Service, ServiceBuilder};
3use crate::structs::{
4 Alert, AvailableLanguages, AvailablePlugins, AvailableThemes, ConfigChanged, FindStatus,
5 LanguageChanged, MeasureWidth, PluginStarted, PluginStoped, ReplaceStatus, ScrollTo, Style,
6 ThemeChanged, Update, UpdateCmds,
7};
8use futures::{
9 future::{self, Either, FutureResult},
10 Future,
11};
12use serde_json::{from_value, to_value, Value};
13
14#[derive(Debug)]
16pub enum XiNotification {
17 Update(Update),
18 ScrollTo(ScrollTo),
19 DefStyle(Style),
20 AvailablePlugins(AvailablePlugins),
21 UpdateCmds(UpdateCmds),
22 PluginStarted(PluginStarted),
23 PluginStoped(PluginStoped),
24 ConfigChanged(ConfigChanged),
25 ThemeChanged(ThemeChanged),
26 Alert(Alert),
27 AvailableThemes(AvailableThemes),
28 FindStatus(FindStatus),
29 ReplaceStatus(ReplaceStatus),
30 AvailableLanguages(AvailableLanguages),
31 LanguageChanged(LanguageChanged),
32}
33
34pub trait Frontend {
37 type NotificationResult: IntoStaticFuture<Item = (), Error = ()>;
38 fn handle_notification(&mut self, notification: XiNotification) -> Self::NotificationResult;
39
40 type MeasureWidthResult: IntoStaticFuture<Item = Vec<Vec<f32>>, Error = ()>;
41 fn handle_measure_width(&mut self, request: MeasureWidth) -> Self::MeasureWidthResult;
42}
43
44pub trait FrontendBuilder {
46 type Frontend: Frontend;
48
49 fn build(self, client: Client) -> Self::Frontend;
51}
52
53impl<B> ServiceBuilder for B
54where
55 B: FrontendBuilder,
56 B::Frontend: Send,
57{
58 type Service = B::Frontend;
59
60 fn build(self, client: InnerClient) -> B::Frontend {
61 <Self as FrontendBuilder>::build(self, Client(client))
62 }
63}
64
65impl<F: Frontend + Send> Service for F {
66 type T = Value;
67 type E = Value;
68 type RequestFuture = Box<dyn Future<Item = Self::T, Error = Self::E> + 'static + Send>;
69 type NotificationFuture = Either<
70 <<F as Frontend>::NotificationResult as IntoStaticFuture>::Future,
71 FutureResult<(), ()>,
72 >;
73
74 fn handle_request(&mut self, method: &str, params: Value) -> Self::RequestFuture {
75 info!("<<< request: method={}, params={}", method, ¶ms);
76 match method {
77 "measure_width" => {
78 match from_value::<MeasureWidth>(params) {
79 Ok(request) => {
80 let future = self
81 .handle_measure_width(request)
82 .into_static_future()
83 .map(|response| {
84 to_value(response).expect("failed to convert response")
87 })
88 .map_err(|_| panic!("errors are not supported"));
89 Box::new(future)
90 }
91 Err(e) => {
92 warn!("failed to deserialize measure_width message: {:?}", e);
93 let err_msg = to_value("invalid measure_width message")
94 .expect("failed to serialize string");
96 Box::new(future::err(err_msg))
97 }
98 }
99 }
100 _ => {
101 let err_msg = to_value(format!("unknown method \"{}\"", method))
102 .expect("failed to serialize string");
104 Box::new(future::err(err_msg))
105 }
106 }
107 }
108
109 #[allow(clippy::cognitive_complexity)]
110 fn handle_notification(&mut self, method: &str, params: Value) -> Self::NotificationFuture {
111 info!("<<< notification: method={}, params={}", method, ¶ms);
112 match method {
113 "update" => match from_value::<Update>(params) {
114 Ok(update) => Either::A(
115 self.handle_notification(XiNotification::Update(update))
116 .into_static_future(),
117 ),
118 Err(e) => {
119 error!("received invalid update notification: {:?}", e);
120 Either::B(future::err(()))
121 }
122 },
123
124 "scroll_to" => match from_value::<ScrollTo>(params) {
125 Ok(scroll_to) => Either::A(
126 self.handle_notification(XiNotification::ScrollTo(scroll_to))
127 .into_static_future(),
128 ),
129 Err(e) => {
130 error!("received invalid scroll_to notification: {:?}", e);
131 Either::B(future::err(()))
132 }
133 },
134
135 "def_style" => match from_value::<Style>(params) {
136 Ok(style) => Either::A(
137 self.handle_notification(XiNotification::DefStyle(style))
138 .into_static_future(),
139 ),
140 Err(e) => {
141 error!("received invalid def_style notification: {:?}", e);
142 Either::B(future::err(()))
143 }
144 },
145 "available_plugins" => match from_value::<AvailablePlugins>(params) {
146 Ok(plugins) => Either::A(
147 self.handle_notification(XiNotification::AvailablePlugins(plugins))
148 .into_static_future(),
149 ),
150 Err(e) => {
151 error!("received invalid available_plugins notification: {:?}", e);
152 Either::B(future::err(()))
153 }
154 },
155 "plugin_started" => match from_value::<PluginStarted>(params) {
156 Ok(plugin) => Either::A(
157 self.handle_notification(XiNotification::PluginStarted(plugin))
158 .into_static_future(),
159 ),
160 Err(e) => {
161 error!("received invalid plugin_started notification: {:?}", e);
162 Either::B(future::err(()))
163 }
164 },
165 "plugin_stoped" => match from_value::<PluginStoped>(params) {
166 Ok(plugin) => Either::A(
167 self.handle_notification(XiNotification::PluginStoped(plugin))
168 .into_static_future(),
169 ),
170 Err(e) => {
171 error!("received invalid plugin_stoped notification: {:?}", e);
172 Either::B(future::err(()))
173 }
174 },
175 "update_cmds" => match from_value::<UpdateCmds>(params) {
176 Ok(cmds) => Either::A(
177 self.handle_notification(XiNotification::UpdateCmds(cmds))
178 .into_static_future(),
179 ),
180 Err(e) => {
181 error!("received invalid update_cmds notification: {:?}", e);
182 Either::B(future::err(()))
183 }
184 },
185 "config_changed" => match from_value::<ConfigChanged>(params) {
186 Ok(config) => Either::A(
187 self.handle_notification(XiNotification::ConfigChanged(config))
188 .into_static_future(),
189 ),
190 Err(e) => {
191 error!("received invalid config_changed notification: {:?}", e);
192 Either::B(future::err(()))
193 }
194 },
195 "theme_changed" => match from_value::<ThemeChanged>(params) {
196 Ok(theme) => Either::A(
197 self.handle_notification(XiNotification::ThemeChanged(theme))
198 .into_static_future(),
199 ),
200 Err(e) => {
201 error!("received invalid theme_changed notification: {:?}", e);
202 Either::B(future::err(()))
203 }
204 },
205 "alert" => match from_value::<Alert>(params) {
206 Ok(alert) => Either::A(
207 self.handle_notification(XiNotification::Alert(alert))
208 .into_static_future(),
209 ),
210 Err(e) => {
211 error!("received invalid alert notification: {:?}", e);
212 Either::B(future::err(()))
213 }
214 },
215 "available_themes" => match from_value::<AvailableThemes>(params) {
216 Ok(themes) => Either::A(
217 self.handle_notification(XiNotification::AvailableThemes(themes))
218 .into_static_future(),
219 ),
220 Err(e) => {
221 error!("received invalid available_themes notification: {:?}", e);
222 Either::B(future::err(()))
223 }
224 },
225 "find_status" => match from_value::<FindStatus>(params) {
226 Ok(find_status) => Either::A(
227 self.handle_notification(XiNotification::FindStatus(find_status))
228 .into_static_future(),
229 ),
230 Err(e) => {
231 error!("received invalid find_status notification: {:?}", e);
232 Either::B(future::err(()))
233 }
234 },
235 "replace_status" => match from_value::<ReplaceStatus>(params) {
236 Ok(replace_status) => Either::A(
237 self.handle_notification(XiNotification::ReplaceStatus(replace_status))
238 .into_static_future(),
239 ),
240 Err(e) => {
241 error!("received invalid replace_status notification: {:?}", e);
242 Either::B(future::err(()))
243 }
244 },
245 "available_languages" => match from_value::<AvailableLanguages>(params) {
246 Ok(available_langs) => Either::A(
247 self.handle_notification(XiNotification::AvailableLanguages(available_langs))
248 .into_static_future(),
249 ),
250 Err(e) => {
251 error!("received invalid available_languages notification: {:?}", e);
252 Either::B(future::err(()))
253 }
254 },
255 "language_changed" => match from_value::<LanguageChanged>(params) {
256 Ok(lang) => Either::A(
257 self.handle_notification(XiNotification::LanguageChanged(lang))
258 .into_static_future(),
259 ),
260 Err(e) => {
261 error!("received invalid language_changed notification: {:?}", e);
262 Either::B(future::err(()))
263 }
264 },
265 _ => Either::B(future::err(())),
266 }
267 }
268}