From 0af9feaa40151dbce89537c2571e2de5f268531a Mon Sep 17 00:00:00 2001 From: maxscout Date: Thu, 14 May 2026 13:45:38 +0000 Subject: [PATCH] Use `PropertyDescriptorWrapper` for json output --- src/bin/hyper_headset_cli.rs | 73 ++++++++++++++++++++++-------------- src/devices/mod.rs | 64 +++++++++++++++++++------------ src/status_tray.rs | 10 ++--- 3 files changed, 90 insertions(+), 57 deletions(-) diff --git a/src/bin/hyper_headset_cli.rs b/src/bin/hyper_headset_cli.rs index 74303b3..7abcb86 100644 --- a/src/bin/hyper_headset_cli.rs +++ b/src/bin/hyper_headset_cli.rs @@ -234,37 +234,52 @@ fn main() { let properties = &device.get_device_state().device_properties; let mut headset_info_string = "{\n".to_string(); - macro_rules! append_json_property { - ($val:expr, $name:ident) => { - if let Some($name) = $val { - headset_info_string += - &format!(" \"{}\": \"{}\",\n", stringify!($name), $name); - } - }; + let mut json_properties: Vec = properties + .get_properties() + .iter() + .map(|property| match property { + hyper_headset::devices::PropertyDescriptorWrapper::Int( + property_descriptor, + _items, + ) => match property_descriptor.data { + Some(data) => format!(" \"{}\": {},\n", property_descriptor.name, data), + _ => "".to_string(), + }, + hyper_headset::devices::PropertyDescriptorWrapper::Bool( + property_descriptor, + ) => match property_descriptor.data { + Some(data) => format!(" \"{}\": {},\n", property_descriptor.name, data), + _ => "".to_string(), + }, + hyper_headset::devices::PropertyDescriptorWrapper::String( + property_descriptor, + ) => match &property_descriptor.data { + Some(data) => { + format!(" \"{}\": \"{}\",\n", property_descriptor.name, data) + } + _ => "".to_string(), + }, + }) + .collect(); + + // The last property needs to end without a comma + match json_properties.last_mut() { + #[allow(unused_assignments)] + Some(mut json_property) => { + let mut last_property = json_property[0..(json_property.len() - 2)].to_string(); + last_property += "\n"; + json_property = &mut last_property; + } + None => { + // Unreachable: + // The program exits if no device is found + // but also has a property for the device being connected + unreachable!(); + } } - append_json_property!(&properties.device_name, device_name); - append_json_property!(properties.battery_level, battery_level); - append_json_property!(properties.charging, charging); - append_json_property!(properties.muted, muted); - append_json_property!(properties.mic_connected, mic_connected); - append_json_property!(properties.pairing_info, pairing_info); - append_json_property!(properties.product_color, product_color); - append_json_property!(properties.side_tone_on, side_tone_on); - append_json_property!(properties.side_tone_volume, side_tone_volume); - append_json_property!(properties.surround_sound, surround_sound); - append_json_property!(properties.voice_prompt_on, voice_prompt_on); - append_json_property!(properties.connected, connected); - append_json_property!(properties.silent, silent); - append_json_property!(properties.noise_gate_active, noise_gate_active); - - // This is the last property so it shouldn't have a comma at the end - // Durations also do not implement `Display` so this needs to be output manually - if let Some(automatic_shutdown_interval) = properties.automatic_shutdown_after { - headset_info_string += &format!( - " \"automatic_shutdown_interval\": \"{}\"\n", - automatic_shutdown_interval.as_secs() - ); + for json_property in json_properties { + headset_info_string += &json_property; } headset_info_string += "}"; diff --git a/src/devices/mod.rs b/src/devices/mod.rs index 6860059..20657ed 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -379,7 +379,8 @@ pub enum PropertyDescriptorWrapper { } pub struct PropertyDescriptor { - pub prefix: &'static str, + pub name: &'static str, + pub pretty_name: &'static str, pub data: Option, pub suffix: &'static str, pub property_type: PropertyType, @@ -389,7 +390,7 @@ pub struct PropertyDescriptor { impl Debug for PropertyDescriptor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PropertyDescriptor") - .field("prefix", &self.prefix) + .field("pretty_name", &self.pretty_name) .field("data", &self.data) .field("suffix", &self.suffix) .field("property_type", &self.property_type) @@ -432,7 +433,8 @@ impl DeviceProperties { pub fn get_properties(&self) -> Vec { vec![ PropertyDescriptorWrapper::String(PropertyDescriptor { - prefix: "Charging status:", + name: "charging_status", + pretty_name: "Charging status", data: self.charging.map(|c| c.to_string()), suffix: "", property_type: PropertyType::AlwaysReadOnly, @@ -440,7 +442,8 @@ impl DeviceProperties { }), PropertyDescriptorWrapper::Int( PropertyDescriptor { - prefix: "Battery level:", + name: "battery_level", + pretty_name: "Battery level:", data: self.battery_level, suffix: "%", property_type: PropertyType::AlwaysReadOnly, @@ -449,7 +452,8 @@ impl DeviceProperties { &[], ), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Muted:", + name: "mic_muted", + pretty_name: "Muted:", data: self.muted, suffix: "", property_type: if self.can_set_mute { @@ -460,7 +464,8 @@ impl DeviceProperties { create_event: &move |mute| Some(DeviceEvent::Muted(mute)), }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Mic connected:", + name: "mic_connected", + pretty_name: "Mic connected:", data: self.mic_connected, suffix: "", property_type: PropertyType::AlwaysReadOnly, @@ -468,7 +473,8 @@ impl DeviceProperties { }), PropertyDescriptorWrapper::Int( PropertyDescriptor { - prefix: "Automatic shutdown after:", + name: "automatic_shutdown_interval", + pretty_name: "Automatic shutdown after:", data: self .automatic_shutdown_after .map(|t| (t.as_secs() / 60) as u8), @@ -488,7 +494,8 @@ impl DeviceProperties { ), PropertyDescriptorWrapper::Int( PropertyDescriptor { - prefix: "Pairing info:", + name: "pairing_info", + pretty_name: "Pairing info:", data: self.pairing_info, suffix: "", property_type: PropertyType::AlwaysReadOnly, @@ -497,14 +504,16 @@ impl DeviceProperties { &[], ), PropertyDescriptorWrapper::String(PropertyDescriptor { - prefix: "Product color:", + name: "product_color", + pretty_name: "Product color:", data: self.product_color.map(|c| c.to_string()), suffix: "", property_type: PropertyType::AlwaysReadOnly, create_event: &|_| None, }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Side tone:", + name: "side_tone_enabled", + pretty_name: "Side tone:", data: self.side_tone_on, suffix: "", property_type: if self.can_set_side_tone { @@ -516,7 +525,8 @@ impl DeviceProperties { }), PropertyDescriptorWrapper::Int( PropertyDescriptor { - prefix: "Side tone volume:", + name: "side_tone_volume", + pretty_name: "Side tone volume:", data: self.side_tone_volume, suffix: "", property_type: if self.can_set_side_tone_volume { @@ -529,7 +539,8 @@ impl DeviceProperties { &[0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250], ), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Surround sound:", + name: "surrond_sound_enabled", + pretty_name: "Surround sound:", data: self.surround_sound, suffix: "", property_type: if self.can_set_surround_sound { @@ -540,7 +551,8 @@ impl DeviceProperties { create_event: &move |enable| Some(DeviceEvent::SurroundSound(enable)), }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Voice prompt:", + name: "voice_prompt_enabled", + pretty_name: "Voice prompt:", data: self.voice_prompt_on, suffix: "", property_type: if self.can_set_voice_prompt { @@ -551,7 +563,8 @@ impl DeviceProperties { create_event: &move |enable| Some(DeviceEvent::VoicePrompt(enable)), }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Playback muted:", + name: "playback_muted", + pretty_name: "Playback muted:", data: self.silent, suffix: "", property_type: if self.can_set_silent_mode { @@ -562,7 +575,8 @@ impl DeviceProperties { create_event: &move |enable| Some(DeviceEvent::Silent(enable)), }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Noise gate active:", + name: "noise_gate_enabled", + pretty_name: "Noise gate active:", data: self.noise_gate_active, suffix: "", property_type: if self.can_set_noise_gate { @@ -573,7 +587,8 @@ impl DeviceProperties { create_event: &move |enable| Some(DeviceEvent::NoiseGateActive(enable)), }), PropertyDescriptorWrapper::Bool(PropertyDescriptor { - prefix: "Connected:", + name: "connected", + pretty_name: "Connected:", data: self.connected, suffix: "", property_type: PropertyType::AlwaysReadOnly, @@ -588,17 +603,17 @@ impl DeviceProperties { .filter_map(|prop| { let (prefix, data, suffix) = match prop { PropertyDescriptorWrapper::Int(property_descriptor, _) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data.map(|v| v.to_string()), property_descriptor.suffix, ), PropertyDescriptorWrapper::Bool(property_descriptor) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data.map(|v| v.to_string()), property_descriptor.suffix, ), PropertyDescriptorWrapper::String(property_descriptor) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data, property_descriptor.suffix, ), @@ -616,19 +631,19 @@ impl DeviceProperties { .filter_map(|prop| { let (prefix, data, suffix, property_type) = match prop { PropertyDescriptorWrapper::Int(property_descriptor, _) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data.map(|v| v.to_string()), property_descriptor.suffix, property_descriptor.property_type, ), PropertyDescriptorWrapper::Bool(property_descriptor) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data.map(|v| v.to_string()), property_descriptor.suffix, property_descriptor.property_type, ), PropertyDescriptorWrapper::String(property_descriptor) => ( - property_descriptor.prefix, + property_descriptor.pretty_name, &property_descriptor.data, property_descriptor.suffix, property_descriptor.property_type, @@ -641,7 +656,10 @@ impl DeviceProperties { } else { "" }; - format!("{:>() diff --git a/src/status_tray.rs b/src/status_tray.rs index e830302..200b9d7 100644 --- a/src/status_tray.rs +++ b/src/status_tray.rs @@ -140,8 +140,8 @@ impl Tray for StatusTray { menu_items.push( StandardItem { label: format!( - "{} {}{}", - property.prefix, current_value, property.suffix + "{}: {}{}", + property.pretty_name, current_value, property.suffix ), enabled: false, activate: Box::new(move |_| { @@ -179,7 +179,7 @@ impl Tray for StatusTray { SubMenu { label: format!( "{} {}{}", - property.prefix, current_value, property.suffix + property.pretty_name, current_value, property.suffix ), enabled: property.property_type == PropertyType::ReadWrite && property.data.is_some(), @@ -199,7 +199,7 @@ impl Tray for StatusTray { StandardItem { label: format!( "{} {}{}", - property.prefix, current_value, property.suffix + property.pretty_name, current_value, property.suffix ), enabled: property.property_type == PropertyType::ReadWrite && property.data.is_some(), @@ -222,7 +222,7 @@ impl Tray for StatusTray { StandardItem { label: format!( "{} {}{}", - property.prefix, current_value, property.suffix + property.pretty_name, current_value, property.suffix ), enabled: false, activate: Box::new(move |_| {