Use PropertyDescriptorWrapper for json output
Some checks failed
Rust / build-macos (push) Has been cancelled
Rust / build-windows (push) Has been cancelled
Rust / build-linux (push) Failing after 14s
Rust / publish-aur (push) Has been skipped

This commit is contained in:
2026-05-14 13:45:38 +00:00
parent aa6168126d
commit 0af9feaa40
3 changed files with 90 additions and 57 deletions

View File

@@ -234,37 +234,52 @@ fn main() {
let properties = &device.get_device_state().device_properties; let properties = &device.get_device_state().device_properties;
let mut headset_info_string = "{\n".to_string(); let mut headset_info_string = "{\n".to_string();
macro_rules! append_json_property { let mut json_properties: Vec<String> = properties
($val:expr, $name:ident) => { .get_properties()
if let Some($name) = $val { .iter()
headset_info_string += .map(|property| match property {
&format!(" \"{}\": \"{}\",\n", stringify!($name), $name); 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); for json_property in json_properties {
append_json_property!(properties.battery_level, battery_level); headset_info_string += &json_property;
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()
);
} }
headset_info_string += "}"; headset_info_string += "}";

View File

@@ -379,7 +379,8 @@ pub enum PropertyDescriptorWrapper {
} }
pub struct PropertyDescriptor<T: 'static> { pub struct PropertyDescriptor<T: 'static> {
pub prefix: &'static str, pub name: &'static str,
pub pretty_name: &'static str,
pub data: Option<T>, pub data: Option<T>,
pub suffix: &'static str, pub suffix: &'static str,
pub property_type: PropertyType, pub property_type: PropertyType,
@@ -389,7 +390,7 @@ pub struct PropertyDescriptor<T: 'static> {
impl<T: Debug> Debug for PropertyDescriptor<T> { impl<T: Debug> Debug for PropertyDescriptor<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PropertyDescriptor") f.debug_struct("PropertyDescriptor")
.field("prefix", &self.prefix) .field("pretty_name", &self.pretty_name)
.field("data", &self.data) .field("data", &self.data)
.field("suffix", &self.suffix) .field("suffix", &self.suffix)
.field("property_type", &self.property_type) .field("property_type", &self.property_type)
@@ -432,7 +433,8 @@ impl DeviceProperties {
pub fn get_properties(&self) -> Vec<PropertyDescriptorWrapper> { pub fn get_properties(&self) -> Vec<PropertyDescriptorWrapper> {
vec![ vec![
PropertyDescriptorWrapper::String(PropertyDescriptor { PropertyDescriptorWrapper::String(PropertyDescriptor {
prefix: "Charging status:", name: "charging_status",
pretty_name: "Charging status",
data: self.charging.map(|c| c.to_string()), data: self.charging.map(|c| c.to_string()),
suffix: "", suffix: "",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
@@ -440,7 +442,8 @@ impl DeviceProperties {
}), }),
PropertyDescriptorWrapper::Int( PropertyDescriptorWrapper::Int(
PropertyDescriptor { PropertyDescriptor {
prefix: "Battery level:", name: "battery_level",
pretty_name: "Battery level:",
data: self.battery_level, data: self.battery_level,
suffix: "%", suffix: "%",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
@@ -449,7 +452,8 @@ impl DeviceProperties {
&[], &[],
), ),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Muted:", name: "mic_muted",
pretty_name: "Muted:",
data: self.muted, data: self.muted,
suffix: "", suffix: "",
property_type: if self.can_set_mute { property_type: if self.can_set_mute {
@@ -460,7 +464,8 @@ impl DeviceProperties {
create_event: &move |mute| Some(DeviceEvent::Muted(mute)), create_event: &move |mute| Some(DeviceEvent::Muted(mute)),
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Mic connected:", name: "mic_connected",
pretty_name: "Mic connected:",
data: self.mic_connected, data: self.mic_connected,
suffix: "", suffix: "",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
@@ -468,7 +473,8 @@ impl DeviceProperties {
}), }),
PropertyDescriptorWrapper::Int( PropertyDescriptorWrapper::Int(
PropertyDescriptor { PropertyDescriptor {
prefix: "Automatic shutdown after:", name: "automatic_shutdown_interval",
pretty_name: "Automatic shutdown after:",
data: self data: self
.automatic_shutdown_after .automatic_shutdown_after
.map(|t| (t.as_secs() / 60) as u8), .map(|t| (t.as_secs() / 60) as u8),
@@ -488,7 +494,8 @@ impl DeviceProperties {
), ),
PropertyDescriptorWrapper::Int( PropertyDescriptorWrapper::Int(
PropertyDescriptor { PropertyDescriptor {
prefix: "Pairing info:", name: "pairing_info",
pretty_name: "Pairing info:",
data: self.pairing_info, data: self.pairing_info,
suffix: "", suffix: "",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
@@ -497,14 +504,16 @@ impl DeviceProperties {
&[], &[],
), ),
PropertyDescriptorWrapper::String(PropertyDescriptor { PropertyDescriptorWrapper::String(PropertyDescriptor {
prefix: "Product color:", name: "product_color",
pretty_name: "Product color:",
data: self.product_color.map(|c| c.to_string()), data: self.product_color.map(|c| c.to_string()),
suffix: "", suffix: "",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
create_event: &|_| None, create_event: &|_| None,
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Side tone:", name: "side_tone_enabled",
pretty_name: "Side tone:",
data: self.side_tone_on, data: self.side_tone_on,
suffix: "", suffix: "",
property_type: if self.can_set_side_tone { property_type: if self.can_set_side_tone {
@@ -516,7 +525,8 @@ impl DeviceProperties {
}), }),
PropertyDescriptorWrapper::Int( PropertyDescriptorWrapper::Int(
PropertyDescriptor { PropertyDescriptor {
prefix: "Side tone volume:", name: "side_tone_volume",
pretty_name: "Side tone volume:",
data: self.side_tone_volume, data: self.side_tone_volume,
suffix: "", suffix: "",
property_type: if self.can_set_side_tone_volume { 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], &[0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250],
), ),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Surround sound:", name: "surrond_sound_enabled",
pretty_name: "Surround sound:",
data: self.surround_sound, data: self.surround_sound,
suffix: "", suffix: "",
property_type: if self.can_set_surround_sound { property_type: if self.can_set_surround_sound {
@@ -540,7 +551,8 @@ impl DeviceProperties {
create_event: &move |enable| Some(DeviceEvent::SurroundSound(enable)), create_event: &move |enable| Some(DeviceEvent::SurroundSound(enable)),
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Voice prompt:", name: "voice_prompt_enabled",
pretty_name: "Voice prompt:",
data: self.voice_prompt_on, data: self.voice_prompt_on,
suffix: "", suffix: "",
property_type: if self.can_set_voice_prompt { property_type: if self.can_set_voice_prompt {
@@ -551,7 +563,8 @@ impl DeviceProperties {
create_event: &move |enable| Some(DeviceEvent::VoicePrompt(enable)), create_event: &move |enable| Some(DeviceEvent::VoicePrompt(enable)),
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Playback muted:", name: "playback_muted",
pretty_name: "Playback muted:",
data: self.silent, data: self.silent,
suffix: "", suffix: "",
property_type: if self.can_set_silent_mode { property_type: if self.can_set_silent_mode {
@@ -562,7 +575,8 @@ impl DeviceProperties {
create_event: &move |enable| Some(DeviceEvent::Silent(enable)), create_event: &move |enable| Some(DeviceEvent::Silent(enable)),
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Noise gate active:", name: "noise_gate_enabled",
pretty_name: "Noise gate active:",
data: self.noise_gate_active, data: self.noise_gate_active,
suffix: "", suffix: "",
property_type: if self.can_set_noise_gate { property_type: if self.can_set_noise_gate {
@@ -573,7 +587,8 @@ impl DeviceProperties {
create_event: &move |enable| Some(DeviceEvent::NoiseGateActive(enable)), create_event: &move |enable| Some(DeviceEvent::NoiseGateActive(enable)),
}), }),
PropertyDescriptorWrapper::Bool(PropertyDescriptor { PropertyDescriptorWrapper::Bool(PropertyDescriptor {
prefix: "Connected:", name: "connected",
pretty_name: "Connected:",
data: self.connected, data: self.connected,
suffix: "", suffix: "",
property_type: PropertyType::AlwaysReadOnly, property_type: PropertyType::AlwaysReadOnly,
@@ -588,17 +603,17 @@ impl DeviceProperties {
.filter_map(|prop| { .filter_map(|prop| {
let (prefix, data, suffix) = match prop { let (prefix, data, suffix) = match prop {
PropertyDescriptorWrapper::Int(property_descriptor, _) => ( PropertyDescriptorWrapper::Int(property_descriptor, _) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data.map(|v| v.to_string()), &property_descriptor.data.map(|v| v.to_string()),
property_descriptor.suffix, property_descriptor.suffix,
), ),
PropertyDescriptorWrapper::Bool(property_descriptor) => ( PropertyDescriptorWrapper::Bool(property_descriptor) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data.map(|v| v.to_string()), &property_descriptor.data.map(|v| v.to_string()),
property_descriptor.suffix, property_descriptor.suffix,
), ),
PropertyDescriptorWrapper::String(property_descriptor) => ( PropertyDescriptorWrapper::String(property_descriptor) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data, &property_descriptor.data,
property_descriptor.suffix, property_descriptor.suffix,
), ),
@@ -616,19 +631,19 @@ impl DeviceProperties {
.filter_map(|prop| { .filter_map(|prop| {
let (prefix, data, suffix, property_type) = match prop { let (prefix, data, suffix, property_type) = match prop {
PropertyDescriptorWrapper::Int(property_descriptor, _) => ( PropertyDescriptorWrapper::Int(property_descriptor, _) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data.map(|v| v.to_string()), &property_descriptor.data.map(|v| v.to_string()),
property_descriptor.suffix, property_descriptor.suffix,
property_descriptor.property_type, property_descriptor.property_type,
), ),
PropertyDescriptorWrapper::Bool(property_descriptor) => ( PropertyDescriptorWrapper::Bool(property_descriptor) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data.map(|v| v.to_string()), &property_descriptor.data.map(|v| v.to_string()),
property_descriptor.suffix, property_descriptor.suffix,
property_descriptor.property_type, property_descriptor.property_type,
), ),
PropertyDescriptorWrapper::String(property_descriptor) => ( PropertyDescriptorWrapper::String(property_descriptor) => (
property_descriptor.prefix, property_descriptor.pretty_name,
&property_descriptor.data, &property_descriptor.data,
property_descriptor.suffix, property_descriptor.suffix,
property_descriptor.property_type, property_descriptor.property_type,
@@ -641,7 +656,10 @@ impl DeviceProperties {
} else { } else {
"" ""
}; };
format!("{:<padding$} {}{}{}", prefix, data, suffix, readonly_marker) format!(
"{:<padding$}: {}{}{}",
prefix, data, suffix, readonly_marker
)
}) })
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()

View File

@@ -140,8 +140,8 @@ impl Tray for StatusTray {
menu_items.push( menu_items.push(
StandardItem { StandardItem {
label: format!( label: format!(
"{} {}{}", "{}: {}{}",
property.prefix, current_value, property.suffix property.pretty_name, current_value, property.suffix
), ),
enabled: false, enabled: false,
activate: Box::new(move |_| { activate: Box::new(move |_| {
@@ -179,7 +179,7 @@ impl Tray for StatusTray {
SubMenu { SubMenu {
label: format!( label: format!(
"{} {}{}", "{} {}{}",
property.prefix, current_value, property.suffix property.pretty_name, current_value, property.suffix
), ),
enabled: property.property_type == PropertyType::ReadWrite enabled: property.property_type == PropertyType::ReadWrite
&& property.data.is_some(), && property.data.is_some(),
@@ -199,7 +199,7 @@ impl Tray for StatusTray {
StandardItem { StandardItem {
label: format!( label: format!(
"{} {}{}", "{} {}{}",
property.prefix, current_value, property.suffix property.pretty_name, current_value, property.suffix
), ),
enabled: property.property_type == PropertyType::ReadWrite enabled: property.property_type == PropertyType::ReadWrite
&& property.data.is_some(), && property.data.is_some(),
@@ -222,7 +222,7 @@ impl Tray for StatusTray {
StandardItem { StandardItem {
label: format!( label: format!(
"{} {}{}", "{} {}{}",
property.prefix, current_value, property.suffix property.pretty_name, current_value, property.suffix
), ),
enabled: false, enabled: false,
activate: Box::new(move |_| { activate: Box::new(move |_| {