1use crate::error::BridgeError;
4use crate::host::ParameterHost;
5use serde::Serialize;
6use wavecraft_protocol::{
7 GetAllParametersResult, GetAudioStatusResult, GetMeterFrameResult, GetOscilloscopeFrameResult,
8 GetParameterParams, GetParameterResult, IpcRequest, IpcResponse, METHOD_GET_ALL_PARAMETERS,
9 METHOD_GET_AUDIO_STATUS, METHOD_GET_METER_FRAME, METHOD_GET_OSCILLOSCOPE_FRAME,
10 METHOD_GET_PARAMETER, METHOD_REQUEST_RESIZE, METHOD_SET_PARAMETER, RequestId,
11 RequestResizeParams, RequestResizeResult, SetParameterParams, SetParameterResult,
12};
13
14pub struct IpcHandler<H: ParameterHost> {
16 host: H,
17}
18
19impl<H: ParameterHost> IpcHandler<H> {
20 pub fn new(host: H) -> Self {
22 Self { host }
23 }
24
25 pub fn handle_request(&self, request: IpcRequest) -> IpcResponse {
30 let result = match request.method.as_str() {
31 METHOD_GET_PARAMETER => self.handle_get_parameter(&request),
32 METHOD_SET_PARAMETER => self.handle_set_parameter(&request),
33 METHOD_GET_ALL_PARAMETERS => self.handle_get_all_parameters(&request),
34 METHOD_GET_METER_FRAME => self.handle_get_meter_frame(&request),
35 METHOD_GET_OSCILLOSCOPE_FRAME => self.handle_get_oscilloscope_frame(&request),
36 METHOD_GET_AUDIO_STATUS => self.handle_get_audio_status(&request),
37 METHOD_REQUEST_RESIZE => self.handle_request_resize(&request),
38 "ping" => self.handle_ping(&request),
39 _ => Err(BridgeError::UnknownMethod(request.method.clone())),
40 };
41
42 match result {
43 Ok(response) => response,
44 Err(err) => IpcResponse::error(request.id, err.to_ipc_error()),
45 }
46 }
47
48 pub fn handle_json(&self, json: &str) -> String {
52 let request: IpcRequest = match serde_json::from_str(json) {
54 Ok(req) => req,
55 Err(_e) => {
56 let response = IpcResponse::error(
58 RequestId::Number(0),
59 wavecraft_protocol::IpcError::parse_error(),
60 );
61 return serde_json::to_string(&response)
64 .expect("IpcResponse serialization is infallible");
65 }
66 };
67
68 let response = self.handle_request(request);
70
71 serde_json::to_string(&response).expect("IpcResponse serialization is infallible")
73 }
74
75 fn handle_get_parameter(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
80 let params: GetParameterParams = match &request.params {
82 Some(value) => serde_json::from_value(value.clone())?,
83 None => {
84 return Err(BridgeError::InvalidParams {
85 method: METHOD_GET_PARAMETER.to_string(),
86 reason: "Missing params".to_string(),
87 });
88 }
89 };
90
91 let param_info = self
93 .host
94 .get_parameter(¶ms.id)
95 .ok_or_else(|| BridgeError::ParameterNotFound(params.id.clone()))?;
96
97 let result = GetParameterResult {
99 id: param_info.id,
100 value: param_info.value,
101 };
102
103 Ok(IpcResponse::success(request.id.clone(), result))
104 }
105
106 fn handle_set_parameter(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
107 let params: SetParameterParams = match &request.params {
109 Some(value) => serde_json::from_value(value.clone())?,
110 None => {
111 return Err(BridgeError::InvalidParams {
112 method: METHOD_SET_PARAMETER.to_string(),
113 reason: "Missing params".to_string(),
114 });
115 }
116 };
117
118 self.host.set_parameter(¶ms.id, params.value)?;
120
121 Ok(IpcResponse::success(
123 request.id.clone(),
124 SetParameterResult {},
125 ))
126 }
127
128 fn handle_get_all_parameters(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
129 let parameters = self.host.get_all_parameters();
130
131 let result = GetAllParametersResult { parameters };
132
133 Ok(IpcResponse::success(request.id.clone(), result))
134 }
135
136 fn handle_get_meter_frame(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
137 let frame = self.host.get_meter_frame();
139
140 let result = GetMeterFrameResult { frame };
141
142 Ok(IpcResponse::success(request.id.clone(), result))
143 }
144
145 fn handle_get_oscilloscope_frame(
146 &self,
147 request: &IpcRequest,
148 ) -> Result<IpcResponse, BridgeError> {
149 let frame = self.host.get_oscilloscope_frame();
150
151 let result = GetOscilloscopeFrameResult { frame };
152
153 Ok(IpcResponse::success(request.id.clone(), result))
154 }
155
156 fn handle_request_resize(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
157 let params: RequestResizeParams = match &request.params {
159 Some(value) => serde_json::from_value(value.clone())?,
160 None => {
161 return Err(BridgeError::InvalidParams {
162 method: METHOD_REQUEST_RESIZE.to_string(),
163 reason: "Missing params".to_string(),
164 });
165 }
166 };
167
168 let accepted = self.host.request_resize(params.width, params.height);
170
171 let result = RequestResizeResult { accepted };
172
173 Ok(IpcResponse::success(request.id.clone(), result))
174 }
175
176 fn handle_get_audio_status(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
177 let result = GetAudioStatusResult {
178 status: self.host.get_audio_status(),
179 };
180
181 Ok(IpcResponse::success(request.id.clone(), result))
182 }
183
184 fn handle_ping(&self, request: &IpcRequest) -> Result<IpcResponse, BridgeError> {
185 #[derive(Serialize)]
187 struct PingResult {
188 pong: bool,
189 }
190
191 Ok(IpcResponse::success(
192 request.id.clone(),
193 PingResult { pong: true },
194 ))
195 }
196}
197
198#[cfg(test)]
203mod tests {
204 use super::*;
205 use wavecraft_protocol::{
206 AudioRuntimePhase, AudioRuntimeStatus, MeterFrame, OscilloscopeFrame, ParameterInfo,
207 ParameterType, RequestId,
208 };
209
210 struct MockHost {
212 params: Vec<ParameterInfo>,
213 }
214
215 impl MockHost {
216 fn new() -> Self {
217 Self {
218 params: vec![
219 ParameterInfo {
220 id: "gain".to_string(),
221 name: "Gain".to_string(),
222 param_type: ParameterType::Float,
223 value: 0.5,
224 default: 0.7,
225 min: 0.0,
226 max: 1.0,
227 unit: Some("dB".to_string()),
228 group: None,
229 variants: None,
230 },
231 ParameterInfo {
232 id: "bypass".to_string(),
233 name: "Bypass".to_string(),
234 param_type: ParameterType::Bool,
235 value: 0.0,
236 default: 0.0,
237 min: 0.0,
238 max: 1.0,
239 unit: None,
240 group: None,
241 variants: None,
242 },
243 ],
244 }
245 }
246 }
247
248 impl ParameterHost for MockHost {
249 fn get_parameter(&self, id: &str) -> Option<ParameterInfo> {
250 self.params.iter().find(|p| p.id == id).cloned()
251 }
252
253 fn set_parameter(&self, id: &str, value: f32) -> Result<(), BridgeError> {
254 let Some(param) = self.params.iter().find(|p| p.id == id) else {
255 return Err(BridgeError::ParameterNotFound(id.to_string()));
256 };
257
258 if !(param.min..=param.max).contains(&value) {
259 return Err(BridgeError::ParameterOutOfRange {
260 id: id.to_string(),
261 value,
262 });
263 }
264
265 Ok(())
267 }
268
269 fn get_all_parameters(&self) -> Vec<ParameterInfo> {
270 self.params.clone()
271 }
272
273 fn get_meter_frame(&self) -> Option<MeterFrame> {
274 None
276 }
277
278 fn get_oscilloscope_frame(&self) -> Option<OscilloscopeFrame> {
279 None
280 }
281
282 fn request_resize(&self, _width: u32, _height: u32) -> bool {
283 true
285 }
286
287 fn get_audio_status(&self) -> Option<AudioRuntimeStatus> {
288 Some(AudioRuntimeStatus {
289 phase: AudioRuntimePhase::RunningFullDuplex,
290 diagnostic: None,
291 sample_rate: Some(44100.0),
292 buffer_size: Some(512),
293 updated_at_ms: 123,
294 })
295 }
296 }
297
298 #[test]
299 fn test_get_parameter_success() {
300 let handler = IpcHandler::new(MockHost::new());
301
302 let request = IpcRequest::new(
303 RequestId::Number(1),
304 METHOD_GET_PARAMETER,
305 Some(serde_json::json!({"id": "gain"})),
306 );
307
308 let response = handler.handle_request(request);
309
310 assert!(response.result.is_some());
311 assert!(response.error.is_none());
312
313 let result: GetParameterResult = serde_json::from_value(response.result.unwrap()).unwrap();
314 assert_eq!(result.id, "gain");
315 assert_eq!(result.value, 0.5);
316 }
317
318 #[test]
319 fn test_get_parameter_not_found() {
320 let handler = IpcHandler::new(MockHost::new());
321
322 let request = IpcRequest::new(
323 RequestId::Number(2),
324 METHOD_GET_PARAMETER,
325 Some(serde_json::json!({"id": "unknown"})),
326 );
327
328 let response = handler.handle_request(request);
329
330 assert!(response.error.is_some());
331 assert!(response.result.is_none());
332
333 let error = response.error.unwrap();
334 assert_eq!(error.code, wavecraft_protocol::ERROR_PARAM_NOT_FOUND);
335 }
336
337 #[test]
338 fn test_set_parameter_success() {
339 let handler = IpcHandler::new(MockHost::new());
340
341 let request = IpcRequest::new(
342 RequestId::Number(3),
343 METHOD_SET_PARAMETER,
344 Some(serde_json::json!({"id": "gain", "value": 0.8})),
345 );
346
347 let response = handler.handle_request(request);
348
349 assert!(response.result.is_some());
350 assert!(response.error.is_none());
351 }
352
353 #[test]
354 fn test_set_parameter_out_of_range() {
355 let handler = IpcHandler::new(MockHost::new());
356
357 let request = IpcRequest::new(
358 RequestId::Number(4),
359 METHOD_SET_PARAMETER,
360 Some(serde_json::json!({"id": "gain", "value": 1.5})),
361 );
362
363 let response = handler.handle_request(request);
364
365 assert!(response.error.is_some());
366 assert!(response.result.is_none());
367
368 let error = response.error.unwrap();
369 assert_eq!(error.code, wavecraft_protocol::ERROR_PARAM_OUT_OF_RANGE);
370 }
371
372 #[test]
373 fn test_unknown_method() {
374 let handler = IpcHandler::new(MockHost::new());
375
376 let request = IpcRequest::new(RequestId::Number(5), "unknownMethod", None);
377
378 let response = handler.handle_request(request);
379
380 assert!(response.error.is_some());
381 let error = response.error.unwrap();
382 assert_eq!(error.code, wavecraft_protocol::ERROR_METHOD_NOT_FOUND);
383 }
384
385 #[test]
386 fn test_ping() {
387 let handler = IpcHandler::new(MockHost::new());
388
389 let request = IpcRequest::new(RequestId::String("ping-1".to_string()), "ping", None);
390
391 let response = handler.handle_request(request);
392
393 assert!(response.result.is_some());
394 assert!(response.error.is_none());
395 }
396
397 #[test]
398 fn test_get_all_parameters() {
399 let handler = IpcHandler::new(MockHost::new());
400
401 let request = IpcRequest::new(RequestId::Number(6), METHOD_GET_ALL_PARAMETERS, None);
402
403 let response = handler.handle_request(request);
404
405 assert!(response.result.is_some());
406
407 let result: GetAllParametersResult =
408 serde_json::from_value(response.result.unwrap()).unwrap();
409 assert_eq!(result.parameters.len(), 2);
410 }
411
412 #[test]
413 fn test_handle_json() {
414 let handler = IpcHandler::new(MockHost::new());
415
416 let json = r#"{"jsonrpc":"2.0","id":1,"method":"getParameter","params":{"id":"gain"}}"#;
417 let response_json = handler.handle_json(json);
418
419 assert!(response_json.contains("\"result\""));
420 assert!(!response_json.contains("\"error\""));
421 }
422
423 #[test]
424 fn test_handle_json_parse_error() {
425 let handler = IpcHandler::new(MockHost::new());
426
427 let json = r#"{"invalid json"#;
428 let response_json = handler.handle_json(json);
429
430 assert!(response_json.contains("\"error\""));
431 }
432
433 #[test]
434 fn test_get_audio_status() {
435 let handler = IpcHandler::new(MockHost::new());
436
437 let request = IpcRequest::new(RequestId::Number(7), METHOD_GET_AUDIO_STATUS, None);
438
439 let response = handler.handle_request(request);
440 assert!(response.result.is_some());
441
442 let result: GetAudioStatusResult =
443 serde_json::from_value(response.result.expect("audio status response should exist"))
444 .expect("audio status result should deserialize");
445 let status = result.status.expect("status should be present");
446 assert_eq!(status.phase, AudioRuntimePhase::RunningFullDuplex);
447 }
448
449 #[test]
450 fn test_get_oscilloscope_frame_none() {
451 let handler = IpcHandler::new(MockHost::new());
452
453 let request = IpcRequest::new(RequestId::Number(8), METHOD_GET_OSCILLOSCOPE_FRAME, None);
454
455 let response = handler.handle_request(request);
456 assert!(response.result.is_some());
457
458 let result: GetOscilloscopeFrameResult =
459 serde_json::from_value(response.result.expect("oscilloscope response should exist"))
460 .expect("oscilloscope result should deserialize");
461 assert!(result.frame.is_none());
462 }
463}