From cdad776df09624b5feab59ee7f2e03dc583e51ac Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 21 Nov 2025 13:55:10 +0900 Subject: [PATCH] commands: implement response parser for `outputs` --- src/commands/audio_output_devices/outputs.rs | 128 ++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/src/commands/audio_output_devices/outputs.rs b/src/commands/audio_output_devices/outputs.rs index b9ca55fc..ba72e290 100644 --- a/src/commands/audio_output_devices/outputs.rs +++ b/src/commands/audio_output_devices/outputs.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, }; pub struct Outputs; @@ -31,6 +32,131 @@ impl Command for Outputs { fn parse_response( _parts: ResponseAttributes<'_>, ) -> Result> { - unimplemented!() + let mut outputs = Vec::new(); + + let mut id: Option = None; + let mut name: Option = None; + let mut plugin: Option = None; + let mut enabled: Option = None; + let mut attributes: HashMap = HashMap::new(); + + for (k, v) in _parts.0.into_iter() { + match k { + "outputid" => { + // Reset and store the previous output if all fields are present + if let (Some(id), Some(name), Some(plugin), Some(enabled)) = + (id.take(), name.take(), plugin.take(), enabled.take()) + { + outputs.push(Output { + id, + name, + plugin, + enabled, + attribute: attributes, + }); + } + attributes = HashMap::new(); + let id_s = expect_property_type!(Some(v), k, Text); + id = Some( + id_s.parse::() + .map_err(|_| ResponseParserError::SyntaxError(0, id_s))?, + ); + } + "outputname" => { + name = Some(expect_property_type!(Some(v), k, Text).to_string()); + } + "plugin" => { + plugin = Some(expect_property_type!(Some(v), k, Text).to_string()); + } + "outputenabled" => { + let val_s = expect_property_type!(Some(v), k, Text); + let val: u64 = val_s + .parse::() + .map_err(|_| ResponseParserError::SyntaxError(0, val_s))?; + enabled = Some(val != 0); + } + "attribute" => { + let value = expect_property_type!(Some(v), k, Text); + let mut parts = value.splitn(2, '='); + let attr_key = parts + .next() + .ok_or(ResponseParserError::SyntaxError(0, &value))? + .to_string(); + let attr_value = parts + .next() + .ok_or(ResponseParserError::SyntaxError(0, &value))? + .to_string(); + attributes.insert(attr_key, attr_value); + } + _ => { + return Err(ResponseParserError::UnexpectedProperty(k)); + } + } + } + + // Store the last output if all fields are present + if let (Some(id), Some(name), Some(plugin), Some(enabled)) = + (id.take(), name.take(), plugin.take(), enabled.take()) + { + outputs.push(Output { + id, + name, + plugin, + enabled, + attribute: attributes, + }); + } + + Ok(outputs) + } +} + +#[cfg(test)] +mod tests { + use indoc::indoc; + + use super::*; + + #[test] + fn test_parse_response() { + let input = indoc! {" + outputid: 0 + outputname: PipeWire Sound Server + plugin: pipewire + outputenabled: 1 + outputid: 1 + outputname: Visualizer feed + plugin: fifo + outputenabled: 1 + attribute: fifo_path=/tmp/empidee-visualizer.fifo + OK + "}; + let result = Outputs::parse_raw_response(input); + assert_eq!( + result, + Ok(vec![ + Output { + id: 0, + name: "PipeWire Sound Server".to_string(), + plugin: "pipewire".to_string(), + enabled: true, + attribute: HashMap::new(), + }, + Output { + id: 1, + name: "Visualizer feed".to_string(), + plugin: "fifo".to_string(), + enabled: true, + attribute: { + let mut map = HashMap::new(); + map.insert( + "fifo_path".to_string(), + "/tmp/empidee-visualizer.fifo".to_string(), + ); + map + }, + }, + ]) + ); } }