aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml2
-rw-r--r--formatable-float/Cargo.toml9
-rw-r--r--formatable-float/src/lib.rs154
-rw-r--r--pulse/Cargo.toml1
-rw-r--r--pulse/src/config.rs175
-rw-r--r--pulse/src/runnable/mod.rs1
7 files changed, 188 insertions, 163 deletions
diff --git a/Cargo.lock b/Cargo.lock
index afcf1a0..48d8cf3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -144,6 +144,14 @@ dependencies = [
]
[[package]]
+name = "formatable-float"
+version = "0.1.0"
+dependencies = [
+ "erased-serde",
+ "serde",
+]
+
+[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -539,6 +547,7 @@ name = "swaystatus-pulse"
version = "0.1.0"
dependencies = [
"erased-serde",
+ "formatable-float",
"serde",
"swaystatus-plugin",
]
diff --git a/Cargo.toml b/Cargo.toml
index 49dc253..e59390a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,9 @@
members = [
"swaystatus",
"swaystatus-plugin",
+ "formatable-float",
"alsa",
"clock",
"pulse"
]
+resolver = "2"
diff --git a/formatable-float/Cargo.toml b/formatable-float/Cargo.toml
new file mode 100644
index 0000000..323bc07
--- /dev/null
+++ b/formatable-float/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "formatable-float"
+version = "0.1.0"
+authors = ["Andreas Grois <andi@grois.info>"]
+edition = "2021"
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+erased-serde = "0.3"
diff --git a/formatable-float/src/lib.rs b/formatable-float/src/lib.rs
new file mode 100644
index 0000000..7b1db21
--- /dev/null
+++ b/formatable-float/src/lib.rs
@@ -0,0 +1,154 @@
+use serde::{Serialize,Deserialize,Serializer,Deserializer};
+use serde::de::Error as DeError;
+use serde::de::Unexpected as DeUnexpect;
+use std::collections::BTreeMap;
+use std::ops::{Add, Sub};
+use std::str::FromStr;
+use std::num::{ParseIntError,IntErrorKind};
+
+#[derive(Serialize, Deserialize)]
+#[serde(tag = "Format")]
+pub enum FormatableFloatValue<KeyTypeMetadata : KeyBackingTypeMetadata> {
+ Off,
+ Numeric {
+ #[serde(rename = "Label")]
+ label : String,
+ #[serde(rename = "DecimalDigits")]
+ digits : u8
+ },
+ Binned {
+ #[serde(rename = "Label")]
+ label: String,
+ #[serde(rename = "PercentToSymbolMap")]
+ bin_symbol_map : BTreeMap<FormatableFloatKey<KeyTypeMetadata>,String>
+ }
+}
+
+#[derive(Debug)]
+pub enum FormattingError {
+ EmptyMap {
+ numeric_fallback : String
+ }
+}
+impl std::fmt::Display for FormattingError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ FormattingError::EmptyMap{numeric_fallback} => { write!(f, "Formatting failed. Empty PercentToSymbolMap. Numeric value: {}", numeric_fallback) }
+ }
+ }
+}
+impl std::error::Error for FormattingError {}
+
+impl<KeyTypeMetadata : KeyBackingTypeMetadata> FormatableFloatValue<KeyTypeMetadata> {
+ pub fn format_float(&self, float : f32) -> Result<Option<String>, FormattingError> {
+ match self {
+ FormatableFloatValue::Numeric{ label, digits } => { Ok(Some(Self::format_float_numeric(float, label, *digits))) }
+ FormatableFloatValue::Binned{ label, bin_symbol_map } => { Some(Self::format_float_binned(float, label, bin_symbol_map)).transpose()}
+ FormatableFloatValue::Off => {Ok(None)}
+ }
+ }
+ pub fn format_float_binned(float : f32, label : &str, bin_symbol_map : &BTreeMap<FormatableFloatKey<KeyTypeMetadata>, String>) -> Result<String,FormattingError> {
+ let value_to_match = FormatableFloatKey::<KeyTypeMetadata>::match_float(float);
+ //first try to find the next lower value.
+ if let Some((_,msg)) = bin_symbol_map.range(..=value_to_match).next_back() {
+ Ok(format!("{}{}",label,msg))
+ }
+ else if let Some((_,msg)) = bin_symbol_map.iter().next() {
+ Ok(format!("{}{}",label,msg))
+ }
+ else {
+ Err(FormattingError::EmptyMap{numeric_fallback : Self::format_float_numeric(float, label, 0) })
+ }
+ }
+ pub fn format_float_numeric(float : f32, label : &str, digits : u8) -> String {
+ let percentage = 100.0*float;
+ format!("{}{:.*}%", label, digits as usize, percentage)
+ }
+}
+
+///Helper trait for conversion from float to integer backing type for binning keys.
+///Needed because Rust seems not to offer a trait that indicates "can be rounded from float"
+///in the standard library. There are thir-party crates that do this, but using a full crate
+///for a few lines of code sounds a bit excessive...
+pub trait BackingTypeFromFloat {
+ fn round_from_float(float : f32) -> Self;
+}
+
+///Metadata description for Keys. Basically a workaround for Rust's lack of
+///constant generics. Having Ord as supertrait is because of the BTreeMap's trait
+///bounds.
+pub trait KeyBackingTypeMetadata : Ord {
+ type BackingType
+ : Ord
+ + Add<Output = Self::BackingType>
+ + Sub<Output = Self::BackingType>
+ + Into<f32>
+ + BackingTypeFromFloat
+ + ToString //TOML needs map keys to be strings...
+ + FromStr<Err = ParseIntError>
+ + std::fmt::Display;
+ const MIN : Self::BackingType;
+ const MAX : Self::BackingType;
+ const FLOAT_MIN : f32;
+ const FLOAT_MAX : f32;
+}
+
+#[derive(Debug,PartialEq,Eq,PartialOrd,Ord)]
+pub struct FormatableFloatKey<BackingType : KeyBackingTypeMetadata>(pub BackingType::BackingType);
+
+impl<BackingType : KeyBackingTypeMetadata> FormatableFloatKey<BackingType> {
+ fn match_float(float : f32) -> Self {
+ let x = (float - BackingType::FLOAT_MIN) / (BackingType::FLOAT_MAX - BackingType::FLOAT_MIN);
+ let cx = x.clamp(0.0,1.0);
+ let interval = BackingType::MAX.into() - BackingType::MIN.into();
+ let offset = interval * cx;
+ let result = BackingType::BackingType::round_from_float(offset) + BackingType::MIN;
+ Self(result)
+ }
+
+}
+/// Custom serializer, as TOML only supports string map keys.
+impl<Metadata : KeyBackingTypeMetadata> Serialize for FormatableFloatKey<Metadata> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where S : Serializer
+ {
+ let string = self.0.to_string();
+ serializer.serialize_str(&string)
+ }
+}
+impl<'de, Metadata> Deserialize<'de> for FormatableFloatKey<Metadata>
+ where Metadata : KeyBackingTypeMetadata,
+{
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where D: Deserializer<'de>
+ {
+ let a = String::deserialize(deserializer)?;
+ match a.parse() {
+ Ok(x) => {
+ if x >= Metadata::MIN && x <= Metadata::MAX {
+ Ok(Self(x))
+ }
+ else {
+ Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX)))
+ }
+ }
+ Err(e) => {
+ match e.kind() {
+ IntErrorKind::Empty => { Err(DeError::missing_field("Bin Map Key")) }
+ IntErrorKind::InvalidDigit => { Err(DeError::invalid_type(DeUnexpect::Str(&a), &"an integer value")) }
+ IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX))) }
+ IntErrorKind::Zero => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("a nonzero integer between {} and {}", Metadata::MIN, Metadata::MAX)))}
+ _ => { Err(DeError::custom("Value could not be parsed")) }
+ }
+ }
+ }
+ }
+}
+macro_rules! impl_key_backing_type_from_float_for {
+ ($( $t:ty ), *) => {
+ $( impl BackingTypeFromFloat for $t {
+ fn round_from_float(float : f32) -> $t { float.round() as $t }
+ } )*
+ }
+}
+impl_key_backing_type_from_float_for!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, i128, u128);
diff --git a/pulse/Cargo.toml b/pulse/Cargo.toml
index c6cff4b..c3b4c12 100644
--- a/pulse/Cargo.toml
+++ b/pulse/Cargo.toml
@@ -10,6 +10,7 @@ edition = "2021"
swaystatus-plugin = { path = '../swaystatus-plugin', version = '*'}
serde = { version = "1.0", features = ["derive"] }
erased-serde = "0.3"
+formatable-float = { path = '../formatable-float', version = '*'}
[lib]
crate-type = ["cdylib"]
diff --git a/pulse/src/config.rs b/pulse/src/config.rs
index 0be7367..fb123e0 100644
--- a/pulse/src/config.rs
+++ b/pulse/src/config.rs
@@ -1,12 +1,8 @@
-use serde::{Serialize,Deserialize,Serializer,Deserializer};
-use serde::de::Error as DeError;
-use serde::de::Unexpected as DeUnexpect;
use std::collections::BTreeMap;
-use swaystatus_plugin::*;
-use std::ops::{Add, Sub};
-use std::str::FromStr;
-use std::num::{ParseIntError,IntErrorKind};
+use serde::{Serialize,Deserialize};
+use swaystatus_plugin::*;
+use formatable_float::{FormatableFloatValue, FormattingError, KeyBackingTypeMetadata, FormatableFloatKey};
#[derive(Serialize, Deserialize)]
#[serde(tag = "Sink")]
@@ -19,24 +15,6 @@ pub(crate) enum Sink {
}
#[derive(Serialize, Deserialize)]
-#[serde(tag = "Format")]
-enum FormatableVolume<KeyTypeMetadata : VolumeKeyBackingTypeMetadata> {
- Off,
- Numeric {
- #[serde(rename = "Label")]
- label : String,
- #[serde(rename = "DecimalDigits")]
- digits : u8
- },
- Binned {
- #[serde(rename = "Label")]
- label: String,
- #[serde(rename = "PercentToSymbolMap")]
- bin_symbol_map : BTreeMap<VolumeKey<KeyTypeMetadata>,String>
- }
-}
-
-#[derive(Serialize, Deserialize)]
enum FieldSorting {
MuteVolumeBalance,
MuteBalanceVolume,
@@ -65,8 +43,8 @@ enum FormatableMute {
pub struct PulseVolumeConfig {
sorting : FieldSorting,
pub(crate) sink : Sink,
- volume : FormatableVolume<VolumeKeyVolume>,
- balance : FormatableVolume<VolumeKeyBalance>,
+ volume : FormatableFloatValue<VolumeKeyVolume>,
+ balance : FormatableFloatValue<VolumeKeyBalance>,
mute : FormatableMute,
}
@@ -109,14 +87,14 @@ impl Default for PulseVolumeConfig {
fn default() -> Self {
PulseVolumeConfig {
sink : Sink::Default,
- volume : FormatableVolume::Numeric { label : String::from(""), digits : 0 },
- balance : FormatableVolume::Binned {
+ volume : FormatableFloatValue::Numeric { label : String::from(""), digits : 0 },
+ balance : FormatableFloatValue::Binned {
label : String::from(" "),
bin_symbol_map : {
let mut a = BTreeMap::new();
- a.insert(VolumeKey(-100),String::from("|.."));
- a.insert(VolumeKey(-10), String::from(".|."));
- a.insert(VolumeKey(10), String::from("..|"));
+ a.insert(FormatableFloatKey(-100),String::from("|.."));
+ a.insert(FormatableFloatKey(-10), String::from(".|."));
+ a.insert(FormatableFloatKey(10), String::from("..|"));
a
}
},
@@ -133,48 +111,6 @@ impl SwayStatusModuleInstance for PulseVolumeConfig {
}
}
-#[derive(Debug)]
-pub(crate) enum FormattingError {
- EmptyMap {
- numeric_fallback : String
- }
-}
-impl std::fmt::Display for FormattingError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- FormattingError::EmptyMap{numeric_fallback} => { write!(f, "Formatting failed. Empty PercentToSymbolMap. Numeric value: {}", numeric_fallback) }
- }
- }
-}
-impl std::error::Error for FormattingError {}
-
-impl<KeyTypeMetadata : VolumeKeyBackingTypeMetadata> FormatableVolume<KeyTypeMetadata> {
- fn format_float(&self, float : f32) -> Result<Option<String>, FormattingError> {
- match self {
- FormatableVolume::Numeric{ label, digits } => { Ok(Some(Self::format_float_numeric(float, label, *digits))) }
- FormatableVolume::Binned{ label, bin_symbol_map } => { Some(Self::format_float_binned(float, label, bin_symbol_map)).transpose()}
- FormatableVolume::Off => {Ok(None)}
- }
- }
- fn format_float_binned(float : f32, label : &str, bin_symbol_map : &BTreeMap<VolumeKey<KeyTypeMetadata>, String>) -> Result<String,FormattingError> {
- let value_to_match = VolumeKey::<KeyTypeMetadata>::match_float(float);
- //first try to find the next lower value.
- if let Some((_,msg)) = bin_symbol_map.range(..=value_to_match).next_back() {
- Ok(format!("{}{}",label,msg))
- }
- else if let Some((_,msg)) = bin_symbol_map.iter().next() {
- Ok(format!("{}{}",label,msg))
- }
- else {
- Err(FormattingError::EmptyMap{numeric_fallback : Self::format_float_numeric(float, label, 0) })
- }
- }
- fn format_float_numeric(float : f32, label : &str, digits : u8) -> String {
- let percentage = 100.0*float;
- format!("{}{:.*}%", label, digits as usize, percentage)
- }
-}
-
impl FormatableMute {
fn format_mute(&self, mute : bool) -> Option<String> {
match self {
@@ -183,45 +119,12 @@ impl FormatableMute {
}
}
}
-///Helper trait for conversion from float to integer backing type for volume binning keys.
-///Needed because Rust seems not to offer a trait that indicates "can be rounded from float"
-///in the standard library. There are thir-party crates that do this, but using a full crate
-///for a few lines of code sounds a bit excessive...
-trait VolumeKeyBackingTypeFromFloat {
- fn round_from_float(float : f32) -> Self;
-}
-macro_rules! impl_volume_key_backing_type_from_float_for {
- ($( $t:ty ), *) => {
- $( impl VolumeKeyBackingTypeFromFloat for $t {
- fn round_from_float(float : f32) -> $t { float.round() as $t }
- } )*
- }
-}
-
-///Metadata description for VolumeKeys. Basically a workaround for Rust's lack of
-///constant generics. Having Ord as supertrait is because of the BTreeMap's trait
-///bounds.
-trait VolumeKeyBackingTypeMetadata : Ord {
- type BackingType
- : Ord
- + Add<Output = Self::BackingType>
- + Sub<Output = Self::BackingType>
- + Into<f32>
- + VolumeKeyBackingTypeFromFloat
- + ToString //TOML needs map keys to be strings...
- + FromStr<Err = ParseIntError>
- + std::fmt::Display;
- const MIN : Self::BackingType;
- const MAX : Self::BackingType;
- const FLOAT_MIN : f32;
- const FLOAT_MAX : f32;
-}
#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct VolumeKeyVolume;
#[derive(PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct VolumeKeyBalance;
-impl VolumeKeyBackingTypeMetadata for VolumeKeyVolume{
+impl KeyBackingTypeMetadata for VolumeKeyVolume{
type BackingType = u8;
const MIN : Self::BackingType = 0;
const MAX : Self::BackingType = 100;
@@ -229,64 +132,10 @@ impl VolumeKeyBackingTypeMetadata for VolumeKeyVolume{
const FLOAT_MAX : f32 = 1.0;
}
-impl VolumeKeyBackingTypeMetadata for VolumeKeyBalance{
+impl KeyBackingTypeMetadata for VolumeKeyBalance{
type BackingType = i8;
const MIN : Self::BackingType = -100;
const MAX : Self::BackingType = 100;
const FLOAT_MIN : f32 = -1.0;
const FLOAT_MAX : f32 = 1.0;
}
-
-impl_volume_key_backing_type_from_float_for!(i8, u8);
-
-#[derive(Debug,PartialEq,Eq,PartialOrd,Ord)]
-struct VolumeKey<BackingType : VolumeKeyBackingTypeMetadata>(BackingType::BackingType);
-
-impl<BackingType : VolumeKeyBackingTypeMetadata> VolumeKey<BackingType> {
- fn match_float(float : f32) -> Self {
- let x = (float - BackingType::FLOAT_MIN) / (BackingType::FLOAT_MAX - BackingType::FLOAT_MIN);
- let cx = x.clamp(0.0,1.0);
- let interval = BackingType::MAX.into() - BackingType::MIN.into();
- let offset = interval * cx;
- let result = BackingType::BackingType::round_from_float(offset) + BackingType::MIN;
- Self(result)
- }
-
-}
-/// Custom serializer, as TOML only supports string map keys.
-impl<Metadata : VolumeKeyBackingTypeMetadata> Serialize for VolumeKey<Metadata> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where S : Serializer
- {
- let string = self.0.to_string();
- serializer.serialize_str(&string)
- }
-}
-impl<'de, Metadata> Deserialize<'de> for VolumeKey<Metadata>
- where Metadata : VolumeKeyBackingTypeMetadata,
-{
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: Deserializer<'de>
- {
- let a = String::deserialize(deserializer)?;
- match a.parse() {
- Ok(x) => {
- if x >= Metadata::MIN && x <= Metadata::MAX {
- Ok(Self(x))
- }
- else {
- Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX)))
- }
- }
- Err(e) => {
- match e.kind() {
- IntErrorKind::Empty => { Err(DeError::missing_field("Bin Map Key")) }
- IntErrorKind::InvalidDigit => { Err(DeError::invalid_type(DeUnexpect::Str(&a), &"an integer value")) }
- IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("an integer equal or larger {} and equal or smaller {}", Metadata::MIN, Metadata::MAX))) }
- IntErrorKind::Zero => { Err(DeError::invalid_value(DeUnexpect::Str(&a), &&*format!("a nonzero integer between {} and {}", Metadata::MIN, Metadata::MAX)))}
- _ => { Err(DeError::custom("Value could not be parsed")) }
- }
- }
- }
- }
-}
diff --git a/pulse/src/runnable/mod.rs b/pulse/src/runnable/mod.rs
index b207874..4a89c5c 100644
--- a/pulse/src/runnable/mod.rs
+++ b/pulse/src/runnable/mod.rs
@@ -3,6 +3,7 @@ use crate::config::*;
use swaystatus_plugin::*;
use crate::communication::*;
use std::convert::TryFrom;
+use formatable_float::FormattingError;
pub mod pulse;
use pulse::{Pulse,MainLoopCreationError, PulseContext, PaContextState, SinkHandle, PulseOperation};