1use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct IpcRequest {
24 pub jsonrpc: String,
26 pub id: RequestId,
28 pub method: String,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub params: Option<serde_json::Value>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct IpcResponse {
38 pub jsonrpc: String,
40 pub id: RequestId,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub result: Option<serde_json::Value>,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub error: Option<IpcError>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct IpcNotification {
53 pub jsonrpc: String,
55 pub method: String,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub params: Option<serde_json::Value>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
64#[serde(untagged)]
65pub enum RequestId {
66 String(String),
67 Number(i64),
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct IpcError {
73 pub code: i32,
75 pub message: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub data: Option<serde_json::Value>,
80}
81
82pub const ERROR_PARSE: i32 = -32700;
88pub const ERROR_INVALID_REQUEST: i32 = -32600;
90pub const ERROR_METHOD_NOT_FOUND: i32 = -32601;
92pub const ERROR_INVALID_PARAMS: i32 = -32602;
94pub const ERROR_INTERNAL: i32 = -32603;
96
97pub const ERROR_PARAM_NOT_FOUND: i32 = -32000;
100pub const ERROR_PARAM_OUT_OF_RANGE: i32 = -32001;
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct GetParameterParams {
114 pub id: String,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct GetParameterResult {
121 pub id: String,
123 pub value: f32,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct SetParameterParams {
134 pub id: String,
136 pub value: f32,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct SetParameterResult {}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct GetAllParametersResult {
151 pub parameters: Vec<ParameterInfo>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ParameterInfo {
158 pub id: String,
160 pub name: String,
162 #[serde(rename = "type")]
164 pub param_type: ParameterType,
165 pub value: f32,
167 pub default: f32,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub unit: Option<String>,
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub group: Option<String>,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "lowercase")]
180pub enum ParameterType {
181 Float,
182 Bool,
183 Enum,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct ParameterChangedNotification {
193 pub id: String,
195 pub value: f32,
197}
198
199pub const METHOD_GET_PARAMETER: &str = "getParameter";
205pub const METHOD_SET_PARAMETER: &str = "setParameter";
207pub const METHOD_GET_ALL_PARAMETERS: &str = "getAllParameters";
209pub const METHOD_GET_METER_FRAME: &str = "getMeterFrame";
211pub const METHOD_REQUEST_RESIZE: &str = "requestResize";
213pub const METHOD_REGISTER_AUDIO: &str = "registerAudio";
215pub const NOTIFICATION_PARAMETER_CHANGED: &str = "parameterChanged";
217pub const NOTIFICATION_METER_UPDATE: &str = "meterUpdate";
219
220impl IpcRequest {
225 pub fn new(
227 id: RequestId,
228 method: impl Into<String>,
229 params: Option<serde_json::Value>,
230 ) -> Self {
231 Self {
232 jsonrpc: "2.0".to_string(),
233 id,
234 method: method.into(),
235 params,
236 }
237 }
238}
239
240impl IpcResponse {
241 pub fn success(id: RequestId, result: impl Serialize) -> Self {
243 Self {
244 jsonrpc: "2.0".to_string(),
245 id,
246 result: Some(serde_json::to_value(result).unwrap()),
247 error: None,
248 }
249 }
250
251 pub fn error(id: RequestId, error: IpcError) -> Self {
253 Self {
254 jsonrpc: "2.0".to_string(),
255 id,
256 result: None,
257 error: Some(error),
258 }
259 }
260}
261
262impl IpcNotification {
263 pub fn new(method: impl Into<String>, params: impl Serialize) -> Self {
265 Self {
266 jsonrpc: "2.0".to_string(),
267 method: method.into(),
268 params: Some(serde_json::to_value(params).unwrap()),
269 }
270 }
271}
272
273impl IpcError {
274 pub fn new(code: i32, message: impl Into<String>) -> Self {
276 Self {
277 code,
278 message: message.into(),
279 data: None,
280 }
281 }
282
283 pub fn with_data(code: i32, message: impl Into<String>, data: impl Serialize) -> Self {
285 Self {
286 code,
287 message: message.into(),
288 data: Some(serde_json::to_value(data).unwrap()),
289 }
290 }
291
292 pub fn parse_error() -> Self {
294 Self::new(ERROR_PARSE, "Parse error")
295 }
296
297 pub fn invalid_request(reason: impl Into<String>) -> Self {
299 Self::new(
300 ERROR_INVALID_REQUEST,
301 format!("Invalid request: {}", reason.into()),
302 )
303 }
304
305 pub fn method_not_found(method: impl AsRef<str>) -> Self {
307 Self::new(
308 ERROR_METHOD_NOT_FOUND,
309 format!("Method not found: {}", method.as_ref()),
310 )
311 }
312
313 pub fn invalid_params(reason: impl Into<String>) -> Self {
315 Self::new(
316 ERROR_INVALID_PARAMS,
317 format!("Invalid params: {}", reason.into()),
318 )
319 }
320
321 pub fn internal_error(reason: impl Into<String>) -> Self {
323 Self::new(ERROR_INTERNAL, format!("Internal error: {}", reason.into()))
324 }
325
326 pub fn param_not_found(id: impl AsRef<str>) -> Self {
328 Self::new(
329 ERROR_PARAM_NOT_FOUND,
330 format!("Parameter not found: {}", id.as_ref()),
331 )
332 }
333
334 pub fn param_out_of_range(id: impl AsRef<str>, value: f32) -> Self {
336 Self::new(
337 ERROR_PARAM_OUT_OF_RANGE,
338 format!("Parameter '{}' value {} out of range", id.as_ref(), value),
339 )
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_request_serialization() {
349 let req = IpcRequest::new(
350 RequestId::Number(1),
351 METHOD_GET_PARAMETER,
352 Some(serde_json::json!({"id": "gain"})),
353 );
354
355 let json = serde_json::to_string(&req).unwrap();
356 assert!(json.contains("\"jsonrpc\":\"2.0\""));
357 assert!(json.contains("\"method\":\"getParameter\""));
358 }
359
360 #[test]
361 fn test_response_serialization() {
362 let resp = IpcResponse::success(
363 RequestId::Number(1),
364 GetParameterResult {
365 id: "gain".to_string(),
366 value: 0.5,
367 },
368 );
369
370 let json = serde_json::to_string(&resp).unwrap();
371 assert!(json.contains("\"jsonrpc\":\"2.0\""));
372 assert!(json.contains("\"result\""));
373 assert!(!json.contains("\"error\""));
374 }
375
376 #[test]
377 fn test_error_response() {
378 let resp = IpcResponse::error(
379 RequestId::String("test".to_string()),
380 IpcError::method_not_found("unknownMethod"),
381 );
382
383 let json = serde_json::to_string(&resp).unwrap();
384 assert!(json.contains("\"error\""));
385 assert!(!json.contains("\"result\""));
386 }
387
388 #[test]
389 fn test_notification_serialization() {
390 let notif = IpcNotification::new(
391 NOTIFICATION_PARAMETER_CHANGED,
392 ParameterChangedNotification {
393 id: "gain".to_string(),
394 value: 0.8,
395 },
396 );
397
398 let json = serde_json::to_string(¬if).unwrap();
399 println!("Notification JSON: {}", json);
400 assert!(json.contains("\"jsonrpc\":\"2.0\""));
401 assert!(json.contains("\"method\":\"parameterChanged\""));
402 }
405
406 #[test]
407 fn test_register_audio_serialization() {
408 let req = IpcRequest::new(
409 RequestId::String("audio-1".to_string()),
410 METHOD_REGISTER_AUDIO,
411 Some(serde_json::json!({
412 "client_id": "dev-audio",
413 "sample_rate": 44100.0,
414 "buffer_size": 512
415 })),
416 );
417
418 let json = serde_json::to_string(&req).unwrap();
419 assert!(json.contains("\"method\":\"registerAudio\""));
420 assert!(json.contains("\"sample_rate\":44100"));
421 }
422
423 #[test]
424 fn test_meter_update_notification() {
425 let notif = IpcNotification::new(
426 NOTIFICATION_METER_UPDATE,
427 MeterUpdateNotification {
428 timestamp_us: 1000,
429 left_peak: 0.5,
430 left_rms: 0.3,
431 right_peak: 0.6,
432 right_rms: 0.4,
433 },
434 );
435
436 let json = serde_json::to_string(¬if).unwrap();
437 assert!(json.contains("\"method\":\"meterUpdate\""));
438 assert!(json.contains("\"left_peak\":0.5"));
439 }
440}
441
442#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
450pub struct MeterFrame {
451 pub peak_l: f32,
453 pub peak_r: f32,
455 pub rms_l: f32,
457 pub rms_r: f32,
459 pub timestamp: u64,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct GetMeterFrameResult {
466 pub frame: Option<MeterFrame>,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct RequestResizeParams {
477 pub width: u32,
479 pub height: u32,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct RequestResizeResult {
486 pub accepted: bool,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct RegisterAudioParams {
497 pub client_id: String,
499 pub sample_rate: f32,
501 pub buffer_size: u32,
503}
504
505#[derive(Debug, Clone, Serialize, Deserialize)]
507pub struct RegisterAudioResult {
508 pub status: String,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct MeterUpdateNotification {
519 pub timestamp_us: u64,
521 pub left_peak: f32,
523 pub left_rms: f32,
525 pub right_peak: f32,
527 pub right_rms: f32,
529}