use std::fmt::Display;
use super::{custom_variable_type::CustomVariableType, state_model_error::StateModelError};
use crate::model::state::StateVariable;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum CustomVariableConfig {
FloatingPoint { initial: OrderedFloat<f64> },
SignedInteger { initial: i64 },
UnsignedInteger { initial: u64 },
Boolean { initial: bool },
}
impl Default for CustomVariableConfig {
fn default() -> Self {
Self::DEFAULT
}
}
impl Display for CustomVariableConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let initial = self
.initial()
.map(|i| format!("{i}"))
.unwrap_or_else(|_| String::from("<invalid initial argument>"));
write!(f, "{}: {}", self.name(), initial)
}
}
impl CustomVariableConfig {
pub const DEFAULT: Self = Self::FloatingPoint {
initial: OrderedFloat(0.0),
};
pub fn name(&self) -> String {
match self {
CustomVariableConfig::FloatingPoint { initial: _ } => {
CustomVariableType::FloatingPoint.to_string()
}
CustomVariableConfig::SignedInteger { initial: _ } => {
CustomVariableType::SignedInteger.to_string()
}
CustomVariableConfig::UnsignedInteger { initial: _ } => {
CustomVariableType::UnsignedInteger.to_string()
}
CustomVariableConfig::Boolean { initial: _ } => CustomVariableType::Boolean.to_string(),
}
}
pub fn initial(&self) -> Result<StateVariable, StateModelError> {
match self {
CustomVariableConfig::FloatingPoint { initial } => self.encode_f64(initial),
CustomVariableConfig::SignedInteger { initial } => self.encode_i64(initial),
CustomVariableConfig::UnsignedInteger { initial } => self.encode_u64(initial),
CustomVariableConfig::Boolean { initial } => self.encode_bool(initial),
}
}
pub fn encode_f64(&self, value: &f64) -> Result<StateVariable, StateModelError> {
match self {
CustomVariableConfig::FloatingPoint { initial: _ } => Ok(StateVariable(*value)),
_ => Err(StateModelError::EncodeError(
CustomVariableType::FloatingPoint.to_string(),
self.name(),
)),
}
}
pub fn encode_i64(&self, value: &i64) -> Result<StateVariable, StateModelError> {
match self {
CustomVariableConfig::SignedInteger { initial: _ } => Ok(StateVariable(*value as f64)),
_ => Err(StateModelError::EncodeError(
CustomVariableType::SignedInteger.to_string(),
self.name(),
)),
}
}
pub fn encode_u64(&self, value: &u64) -> Result<StateVariable, StateModelError> {
match self {
CustomVariableConfig::UnsignedInteger { initial: _ } => {
Ok(StateVariable(*value as f64))
}
_ => Err(StateModelError::EncodeError(
CustomVariableType::UnsignedInteger.to_string(),
self.name(),
)),
}
}
pub fn encode_bool(&self, value: &bool) -> Result<StateVariable, StateModelError> {
match self {
CustomVariableConfig::Boolean { initial: _ } => {
if *value {
Ok(StateVariable(1.0))
} else {
Ok(StateVariable(0.0))
}
}
_ => Err(StateModelError::EncodeError(
CustomVariableType::Boolean.to_string(),
self.name(),
)),
}
}
pub fn decode_f64(&self, value: &StateVariable) -> Result<f64, StateModelError> {
match self {
CustomVariableConfig::FloatingPoint { initial: _ } => Ok(value.0),
_ => Err(StateModelError::DecodeError(
*value,
CustomVariableType::FloatingPoint.to_string(),
self.name(),
)),
}
}
pub fn decode_i64(&self, value: &StateVariable) -> Result<i64, StateModelError> {
match self {
CustomVariableConfig::SignedInteger { initial: _ } => Ok(value.0 as i64),
_ => Err(StateModelError::DecodeError(
*value,
CustomVariableType::SignedInteger.to_string(),
self.name(),
)),
}
}
pub fn decode_u64(&self, value: &StateVariable) -> Result<u64, StateModelError> {
match self {
CustomVariableConfig::UnsignedInteger { initial: _ } => {
if value < &StateVariable::ZERO {
Err(StateModelError::ValueError(
*value,
CustomVariableType::UnsignedInteger.to_string(),
))
} else {
Ok(value.0 as u64)
}
}
_ => Err(StateModelError::DecodeError(
*value,
CustomVariableType::UnsignedInteger.to_string(),
self.name(),
)),
}
}
pub fn decode_bool(&self, value: &StateVariable) -> Result<bool, StateModelError> {
match self {
CustomVariableConfig::Boolean { initial: _ } => {
let is_false = value.0 == 0.0;
if is_false {
Ok(false)
} else {
Ok(true)
}
}
_ => Err(StateModelError::DecodeError(
*value,
CustomVariableType::Boolean.to_string(),
self.name(),
)),
}
}
}