use crate::{
message::InputCommandType,
util::{
range::RangeInclusive,
small_vec_enum_map::{SmallVecEnumMap, VariantKey},
},
};
use enumflags2::BitFlags;
use getset::{CopyGetters, Getters, MutGetters, Setters};
use serde::{Deserialize, Serialize};
#[derive(
Clone, Debug, Default, Getters, MutGetters, CopyGetters, Setters, Serialize, Deserialize,
)]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeature {
#[getset(get_copy = "pub")]
feature_index: u32,
#[getset(get = "pub", get_mut = "pub(super)")]
#[serde(default, rename = "FeatureDescription")]
description: String,
#[serde(skip_serializing_if = "SmallVecEnumMap::is_empty", default)]
output: SmallVecEnumMap<DeviceFeatureOutput, 1>,
#[serde(skip_serializing_if = "SmallVecEnumMap::is_empty", default)]
input: SmallVecEnumMap<DeviceFeatureInput, 1>,
}
impl DeviceFeature {
pub fn new(
index: u32,
description: &str,
output: &SmallVecEnumMap<DeviceFeatureOutput, 1>,
input: &SmallVecEnumMap<DeviceFeatureInput, 1>,
) -> Self {
Self {
feature_index: index,
description: description.to_owned(),
output: output.clone(),
input: input.clone(),
}
}
pub fn contains_output(&self, output_type: OutputType) -> bool {
self.output.contains_key(&output_type)
}
pub fn contains_input(&self, input_type: InputType) -> bool {
self.input.contains_key(&input_type)
}
pub fn get_output(&self, output_type: OutputType) -> Option<&DeviceFeatureOutput> {
self.output.find_by_key(&output_type)
}
pub fn get_output_limits(
&self,
output_type: OutputType,
) -> Option<&dyn DeviceFeatureOutputLimits> {
self.output.find_by_key(&output_type).map(|o| o.as_limits())
}
pub fn get_input(&self, input_type: InputType) -> Option<&DeviceFeatureInput> {
self.input.find_by_key(&input_type)
}
pub fn contains_any_output(&self) -> bool {
!self.output.is_empty()
}
pub fn contains_any_input(&self) -> bool {
!self.input.is_empty()
}
}
pub trait DeviceFeatureOutputLimits {
fn step_count(&self) -> u32;
fn step_limit(&self) -> RangeInclusive<i32>;
}
#[derive(Serialize, Deserialize, Clone, Debug, Getters)]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureOutputValueProperties {
#[getset(get = "pub")]
value: RangeInclusive<i32>,
}
impl DeviceFeatureOutputValueProperties {
pub fn new(value: RangeInclusive<i32>) -> Self {
DeviceFeatureOutputValueProperties { value }
}
}
impl DeviceFeatureOutputLimits for DeviceFeatureOutputValueProperties {
fn step_count(&self) -> u32 {
self.value.end() as u32
}
fn step_limit(&self) -> RangeInclusive<i32> {
self.value
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Getters)]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureOutputHwPositionWithDurationProperties {
#[getset(get = "pub")]
value: RangeInclusive<i32>,
#[getset(get = "pub")]
duration: RangeInclusive<i32>,
}
impl DeviceFeatureOutputHwPositionWithDurationProperties {
pub fn new(position: RangeInclusive<i32>, duration: RangeInclusive<i32>) -> Self {
DeviceFeatureOutputHwPositionWithDurationProperties {
value: position,
duration,
}
}
}
impl DeviceFeatureOutputLimits for DeviceFeatureOutputHwPositionWithDurationProperties {
fn step_count(&self) -> u32 {
self.value.end() as u32
}
fn step_limit(&self) -> RangeInclusive<i32> {
self.value
}
}
#[derive(Clone, Debug, Serialize, Deserialize, EnumDiscriminants)]
#[strum_discriminants(name(OutputType))]
#[strum_discriminants(vis(pub))]
#[strum_discriminants(derive(Display, Hash, EnumIter, EnumString, Serialize, Deserialize))]
pub enum DeviceFeatureOutput {
Vibrate(DeviceFeatureOutputValueProperties),
Rotate(DeviceFeatureOutputValueProperties),
Oscillate(DeviceFeatureOutputValueProperties),
Constrict(DeviceFeatureOutputValueProperties),
Temperature(DeviceFeatureOutputValueProperties),
Led(DeviceFeatureOutputValueProperties),
Position(DeviceFeatureOutputValueProperties),
HwPositionWithDuration(DeviceFeatureOutputHwPositionWithDurationProperties),
Spray(DeviceFeatureOutputValueProperties),
}
impl DeviceFeatureOutput {
pub fn output_type(&self) -> OutputType {
OutputType::from(self)
}
fn as_limits(&self) -> &dyn DeviceFeatureOutputLimits {
match self {
DeviceFeatureOutput::Vibrate(v) => v,
DeviceFeatureOutput::Rotate(v) => v,
DeviceFeatureOutput::Oscillate(v) => v,
DeviceFeatureOutput::Constrict(v) => v,
DeviceFeatureOutput::Temperature(v) => v,
DeviceFeatureOutput::Led(v) => v,
DeviceFeatureOutput::Position(v) => v,
DeviceFeatureOutput::HwPositionWithDuration(v) => v,
DeviceFeatureOutput::Spray(v) => v,
}
}
}
impl VariantKey for DeviceFeatureOutput {
type Key = OutputType;
fn variant_key(&self) -> OutputType {
self.output_type()
}
}
#[derive(
Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters, Serialize, Deserialize,
)]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureInputProperties {
#[getset(get = "pub", get_mut = "pub(super)")]
value: Vec<RangeInclusive<i32>>,
#[getset(get = "pub")]
#[serde(with = "crate::util::serializers::bitflags_seq")]
command: BitFlags<InputCommandType>,
}
impl DeviceFeatureInputProperties {
pub fn new(value: &[RangeInclusive<i32>], sensor_commands: &BitFlags<InputCommandType>) -> Self {
Self {
value: value.to_owned(),
command: *sensor_commands,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, EnumDiscriminants)]
#[strum_discriminants(name(InputType))]
#[strum_discriminants(vis(pub))]
#[strum_discriminants(derive(Display, Hash, EnumIter, EnumString, Serialize, Deserialize))]
pub enum DeviceFeatureInput {
Battery(DeviceFeatureInputProperties),
Rssi(DeviceFeatureInputProperties),
Button(DeviceFeatureInputProperties),
Pressure(DeviceFeatureInputProperties),
Depth(DeviceFeatureInputProperties),
Position(DeviceFeatureInputProperties),
}
impl DeviceFeatureInput {
pub fn input_type(&self) -> InputType {
InputType::from(self)
}
pub fn command(&self) -> &BitFlags<InputCommandType> {
match self {
DeviceFeatureInput::Battery(p) => p.command(),
DeviceFeatureInput::Rssi(p) => p.command(),
DeviceFeatureInput::Button(p) => p.command(),
DeviceFeatureInput::Pressure(p) => p.command(),
DeviceFeatureInput::Depth(p) => p.command(),
DeviceFeatureInput::Position(p) => p.command(),
}
}
}
impl VariantKey for DeviceFeatureInput {
type Key = InputType;
fn variant_key(&self) -> InputType {
self.input_type()
}
}