1use std::fmt;
2
3use bsp_types::{
4 BuildTargetDidChange, LogMessage, PublishDiagnostics, ShowMessage, TaskFinish, TaskProgress,
5 TaskStart,
6};
7use serde::{
8 de::{Error as DeError, MapAccess, Visitor},
9 ser::SerializeStruct,
10 Deserialize, Deserializer, Serialize,
11};
12use serde_json::Value;
13
14use crate::Message;
15
16#[derive(Debug, Clone)]
17pub enum Notification {
18 Exit,
19 Initialized,
20 ShowMessage(ShowMessage),
21 LogMessage(LogMessage),
22 PublishDiagnostics(PublishDiagnostics),
23 TaskStart(TaskStart),
24 TaskFinish(TaskFinish),
25 TaskProgress(TaskProgress),
26 BuildTargetDidChange(BuildTargetDidChange),
27 Custom(&'static str, Value),
28}
29
30impl Notification {
31 pub fn method(&self) -> &'static str {
32 use Notification::*;
33 match self {
34 Exit => "build/exit",
35 Initialized => "build/initialized",
36 ShowMessage(_) => "build/showMessage",
37 LogMessage(_) => "build/logMessage",
38 PublishDiagnostics(_) => "build/publishDiagnostics",
39 TaskStart(_) => "build/taskStart",
40 TaskFinish(_) => "build/taskFinish",
41 TaskProgress(_) => "build/taskProgressing",
42 BuildTargetDidChange(_) => "buildTarget/didChange",
43 Custom(m, _) => m,
44 }
45 }
46}
47
48impl From<(&'static str, Value)> for Notification {
49 fn from(v: (&'static str, Value)) -> Self {
50 Self::Custom(v.0, v.1)
51 }
52}
53
54impl From<&str> for Notification {
55 fn from(msg: &str) -> Self {
56 match msg {
57 "build/exit" => Self::Exit,
58 "build/initialized" => Self::Initialized,
59 _ => panic!("Only exit and initialized supported."),
60 }
61 }
62}
63
64impl From<Notification> for Message {
65 fn from(notification: Notification) -> Self {
66 Self::Notification(notification)
67 }
68}
69
70impl From<(&'static str, Value)> for Message {
71 fn from(v: (&'static str, Value)) -> Self {
72 Self::Notification(Notification::Custom(v.0, v.1))
73 }
74}
75
76macro_rules! convertible {
77 ($p:ident) => {
78 impl From<$p> for Notification {
79 fn from(msg: $p) -> Self {
80 Self::$p(msg)
81 }
82 }
83 impl From<$p> for Message {
84 fn from(msg: $p) -> Self {
85 Self::Notification(crate::Notification::$p(msg))
86 }
87 }
88 };
89}
90
91convertible!(ShowMessage);
92convertible!(LogMessage);
93convertible!(PublishDiagnostics);
94convertible!(TaskStart);
95convertible!(TaskFinish);
96convertible!(TaskProgress);
97convertible!(BuildTargetDidChange);
98
99impl Serialize for Notification {
100 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
101 where
102 S: serde::Serializer,
103 {
104 let mut obj = s.serialize_struct("Notification", 2)?;
105 obj.serialize_field("method", self.method())?;
106
107 use Notification::*;
108 match self {
109 Exit | Initialized => {}
110 ShowMessage(m) => obj.serialize_field("params", m)?,
111 LogMessage(m) => obj.serialize_field("params", m)?,
112 PublishDiagnostics(m) => obj.serialize_field("params", m)?,
113 TaskStart(m) => obj.serialize_field("params", m)?,
114 TaskFinish(m) => obj.serialize_field("params", m)?,
115 TaskProgress(m) => obj.serialize_field("params", m)?,
116 BuildTargetDidChange(m) => obj.serialize_field("params", m)?,
117 Custom(_, m) => obj.serialize_field("params", m)?,
118 };
119
120 obj.end()
121 }
122}
123
124#[cfg(test)]
125mod se {
126 use super::*;
127 #[test]
128 fn initialized() {
129 let value = &Notification::Initialized;
130 let result = serde_json::to_string(value).unwrap();
131 assert_eq!(result, "{\"method\":\"build/initialized\"}");
132 }
133
134 #[test]
135 fn exit() {
136 let value = &Notification::Exit;
137 let result = serde_json::to_string(value).unwrap();
138 assert_eq!(result, "{\"method\":\"build/exit\"}");
139 }
140
141 #[test]
142 fn show_message() {
143 let value = &Notification::TaskStart(TaskStart::new("some_id"));
144 let result = serde_json::to_string(value).unwrap();
145 assert_eq!(
146 result,
147 "{\"method\":\"build/taskStart\",\"params\":{\"taskId\":{\"id\":\"some_id\"}}}"
148 );
149 }
150
151 #[test]
152 fn custom() {
153 let value = &Notification::Custom("custom".into(), Value::Null);
154 let result = serde_json::to_string(value).unwrap();
155 assert_eq!(result, "{\"method\":\"custom\",\"params\":null}");
156 }
157}
158
159impl<'de> Deserialize<'de> for Notification {
160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161 where
162 D: Deserializer<'de>,
163 {
164 const FIELDS: &'static [&'static str] = &["params", "method"];
165 enum Field {
166 Method,
167 Params,
168 Other,
169 }
170
171 impl<'de> Deserialize<'de> for Field {
172 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
173 where
174 D: Deserializer<'de>,
175 {
176 struct FieldVisitor;
177
178 impl<'de> Visitor<'de> for FieldVisitor {
179 type Value = Field;
180
181 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
182 formatter.write_str("method and params")
183 }
184
185 fn visit_str<E>(self, value: &str) -> Result<Field, E>
186 where
187 E: DeError,
188 {
189 match value {
190 "method" => Ok(Field::Method),
191 "params" => Ok(Field::Params),
192 _ => Ok(Field::Other),
193 }
194 }
195 }
196
197 deserializer.deserialize_identifier(FieldVisitor)
198 }
199 }
200
201 struct NotificationVisitor;
202
203 impl<'de> Visitor<'de> for NotificationVisitor {
204 type Value = Notification;
205
206 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
207 formatter.write_str("struct Notification")
208 }
209
210 fn visit_map<V>(self, mut map: V) -> Result<Notification, V::Error>
211 where
212 V: MapAccess<'de>,
213 {
214 let mut params: Option<serde_json::Value> = None; let mut method: Option<String> = None; while let Some(key) = map.next_key()? {
217 match key {
218 Field::Params => {
219 if params.is_some() {
220 return Err(DeError::duplicate_field("params"));
221 }
222 params = Some(map.next_value()?);
223 }
224 Field::Method => {
225 if method.is_some() {
226 return Err(DeError::duplicate_field("method"));
227 }
228 method = Some(map.next_value()?);
229 }
230 _ => (),
231 }
232 }
233
234 let method = method.ok_or_else(|| DeError::missing_field("method"))?;
235 let params = match params {
236 Some(v) => v,
237 None => {
238 if &method != "build/exit" && &method != "build/initialized" {
239 return Err(DeError::missing_field("params"));
240 }
241 serde_json::Value::Null
242 }
243 };
244
245 fn de<'a, T: Deserialize<'a>, E: DeError>(p: serde_json::Value) -> Result<T, E> {
246 T::deserialize(p).map_err(DeError::custom)
247 }
248
249 use Notification::*;
250 Ok(match method.as_str() {
251 "build/exit" => Exit,
252 "build/initialized" => Initialized,
253 "build/showMessage" => ShowMessage(de(params)?),
254 "build/logMessage" => LogMessage(de(params)?),
255 "build/publishDiagnostics" => PublishDiagnostics(de(params)?),
256 "build/taskStart" => TaskStart(de(params)?),
257 "build/taskFinish" => TaskFinish(de(params)?),
258 "build/taskProgressing" => TaskProgress(de(params)?),
259 "buildTarget/didChange" => BuildTargetDidChange(de(params)?),
260 _ => Custom(Box::leak(method.into_boxed_str()), params),
261 })
262 }
263 }
264
265 deserializer.deserialize_struct("Notification", FIELDS, NotificationVisitor)
266 }
267}
268
269#[cfg(test)]
270mod de {
271 use super::*;
272 #[test]
273 fn initialized_without_params() {
274 let value = "{\"method\":\"build/initialized\"}";
275 let msg = serde_json::from_str(value).unwrap();
276 assert!(matches!(msg, Notification::Initialized));
277 }
278
279 #[test]
280 fn initialized_with_params() {
281 let value = serde_json::json!({
282 "jsonrpc": "2.0",
283 "method":"build/initialized"
284 });
285 let result = serde_json::from_value(value).unwrap();
286 assert!(matches!(result, Notification::Initialized));
287 }
288
289 #[test]
290 fn show_message() {
291 let value = "{\"method\":\"build/taskStart\",\"params\":{\"taskId\":{\"id\":\"some_id\"}}}";
292 let result = serde_json::from_str::<Notification>(value).unwrap();
293 assert!(matches!(result, Notification::TaskStart(TaskStart { .. })));
294 }
295}