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