use std::fmt::{Debug, Display};
use std::sync::Arc;
use four_cc::FourCC;
use super::{formatters, Parameter, ParameterType, ParameterValueUpdate};
#[derive(Clone)]
pub struct BooleanParameter {
id: FourCC,
name: &'static str,
default: bool,
value_to_string: Option<fn(bool) -> String>,
string_to_value: Option<fn(&str) -> Option<bool>>,
}
impl Debug for BooleanParameter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BooleanParameter")
.field("id", &self.id)
.field("name", &self.name)
.field("default", &self.default)
.field("value_to_string", &self.value_to_string.is_some())
.field("string_to_value", &self.string_to_value.is_some())
.finish()
}
}
impl BooleanParameter {
pub const fn new(id: FourCC, name: &'static str, default: bool) -> Self {
Self {
id,
name,
default,
value_to_string: None,
string_to_value: None,
}
}
pub const fn with_formatter(mut self, formatter: formatters::BooleanFormatter) -> Self {
self.value_to_string = Some(formatter.0);
self.string_to_value = Some(formatter.1);
self
}
#[must_use]
pub fn value_update(&self, value: bool) -> (FourCC, ParameterValueUpdate) {
(self.id, ParameterValueUpdate::Raw(Arc::new(value)))
}
pub const fn id(&self) -> FourCC {
self.id
}
pub const fn default_value(&self) -> bool {
self.default
}
pub const fn normalize_value(&self, value: bool) -> f32 {
if value {
1.0
} else {
0.0
}
}
pub fn denormalize_value(&self, normalized: f32) -> bool {
assert!((0.0..=1.0).contains(&normalized));
normalized >= 0.5
}
pub fn value_to_string(&self, value: bool) -> String {
match &self.value_to_string {
Some(f) => f(value),
None => {
if value {
"ON".to_string()
} else {
"OFF".to_string()
}
}
}
}
pub fn string_to_value(&self, string: &str) -> Option<bool> {
let value = match &self.string_to_value {
Some(f) => f(string.trim()),
None => {
let string = string.trim();
if string.eq_ignore_ascii_case("ON") {
Some(true)
} else if string.eq_ignore_ascii_case("OFF") {
Some(false)
} else {
string.parse::<bool>().ok()
}
}
}?;
Some(value)
}
}
impl Parameter for BooleanParameter {
fn id(&self) -> FourCC {
self.id
}
fn name(&self) -> &'static str {
self.name
}
fn parameter_type(&self) -> ParameterType {
ParameterType::Boolean
}
fn default_value(&self) -> f32 {
self.normalize_value(self.default)
}
fn value_to_string(&self, normalized: f32, _include_unit: bool) -> String {
let value = self.denormalize_value(normalized.clamp(0.0, 1.0));
self.value_to_string(value)
}
fn string_to_value(&self, string: String) -> Option<f32> {
let value = self.string_to_value(&string)?;
Some(self.normalize_value(value))
}
fn dyn_clone(&self) -> Box<dyn Parameter> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone)]
pub struct BooleanParameterValue {
description: BooleanParameter,
value: bool,
}
impl BooleanParameterValue {
pub fn from_description(description: BooleanParameter) -> Self {
let value = description.default_value();
Self { value, description }
}
pub fn with_value(mut self, value: bool) -> Self {
self.value = value;
self
}
pub fn description(&self) -> &BooleanParameter {
&self.description
}
#[inline(always)]
pub fn value(&self) -> bool {
self.value
}
pub fn set_value(&mut self, value: bool) {
self.value = value;
}
pub fn apply_update(&mut self, update: &ParameterValueUpdate) {
match update {
ParameterValueUpdate::Raw(raw) => {
if let Some(value) = raw.downcast_ref::<bool>() {
self.set_value(*value);
} else {
log::warn!(
"Invalid value type for boolean parameter '{}'",
self.description.id()
);
}
}
ParameterValueUpdate::Normalized(normalized) => {
let value = self
.description
.denormalize_value(normalized.clamp(0.0, 1.0));
self.set_value(value);
}
}
}
}
impl Display for BooleanParameterValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.description.value_to_string(self.value))
}
}