use crate::{message::InputCommandType, util::range_serialize::*};
use derive_builder::Builder;
use getset::{CopyGetters, Getters, MutGetters, Setters};
use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq};
use std::{collections::HashSet, hash::Hash, ops::RangeInclusive};
#[derive(
Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter, EnumString,
)]
pub enum OutputType {
Unknown,
#[serde(alias = "vibrate")]
Vibrate,
#[serde(alias = "rotate")]
Rotate,
#[serde(alias = "oscillate")]
Oscillate,
#[serde(alias = "constrict")]
Constrict,
#[serde(alias = "temperature")]
Temperature,
#[serde(alias = "led")]
Led,
#[serde(alias = "position")]
Position,
#[serde(alias = "hw_position_with_duration")]
HwPositionWithDuration,
#[serde(alias = "spray")]
Spray,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, Hash, EnumIter)]
pub enum InputType {
Unknown,
#[serde(alias = "battery")]
Battery,
#[serde(alias = "rssi")]
Rssi,
#[serde(alias = "button")]
Button,
#[serde(alias = "pressure")]
Pressure,
#[serde(alias = "depth")]
Depth,
#[serde(alias = "position")]
Position,
}
#[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,
#[getset(get = "pub")]
#[serde(skip_serializing_if = "Option::is_none")]
output: Option<DeviceFeatureOutput>,
#[getset(get = "pub")]
#[serde(skip_serializing_if = "Option::is_none")]
input: Option<DeviceFeatureInput>,
}
impl DeviceFeature {
pub fn new(
index: u32,
description: &str,
output: &Option<DeviceFeatureOutput>,
input: &Option<DeviceFeatureInput>,
) -> Self {
Self {
feature_index: index,
description: description.to_owned(),
output: output.clone(),
input: input.clone(),
}
}
}
fn range_sequence_serialize<S>(
range_vec: &Vec<RangeInclusive<i32>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(range_vec.len()))?;
for range in range_vec {
seq.serialize_element(&vec![*range.start(), *range.end()])?;
}
seq.end()
}
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")]
#[serde(serialize_with = "range_serialize")]
value: RangeInclusive<i32>,
}
impl DeviceFeatureOutputValueProperties {
pub fn new(value: &RangeInclusive<i32>) -> Self {
DeviceFeatureOutputValueProperties {
value: value.clone(),
}
}
pub fn step_count(&self) -> u32 {
*self.value.end() as u32
}
}
impl DeviceFeatureOutputLimits for DeviceFeatureOutputValueProperties {
fn step_count(&self) -> u32 {
self.step_count()
}
fn step_limit(&self) -> RangeInclusive<i32> {
self.value.clone()
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Getters)]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureOutputHwPositionWithDurationProperties {
#[getset(get = "pub")]
#[serde(serialize_with = "range_serialize")]
value: RangeInclusive<i32>,
#[getset(get = "pub")]
#[serde(serialize_with = "range_serialize")]
duration: RangeInclusive<i32>,
}
impl DeviceFeatureOutputHwPositionWithDurationProperties {
pub fn new(position: &RangeInclusive<i32>, duration: &RangeInclusive<i32>) -> Self {
DeviceFeatureOutputHwPositionWithDurationProperties {
value: position.clone(),
duration: duration.clone(),
}
}
pub fn step_count(&self) -> u32 {
*self.value.end() as u32
}
}
impl DeviceFeatureOutputLimits for DeviceFeatureOutputHwPositionWithDurationProperties {
fn step_count(&self) -> u32 {
self.step_count()
}
fn step_limit(&self) -> RangeInclusive<i32> {
self.value.clone()
}
}
#[derive(Clone, Debug, Getters, Setters, Default, Serialize, Deserialize, Builder)]
#[builder(setter(strip_option), default)]
#[getset(get = "pub")]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureOutput {
#[serde(skip_serializing_if = "Option::is_none")]
vibrate: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
rotate: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
oscillate: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
constrict: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
temperature: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
led: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
position: Option<DeviceFeatureOutputValueProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
hw_position_with_duration: Option<DeviceFeatureOutputHwPositionWithDurationProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
spray: Option<DeviceFeatureOutputValueProperties>,
}
macro_rules! as_output_limits {
($opt:expr) => {
$opt.as_ref().map(|x| x as &dyn DeviceFeatureOutputLimits)
};
}
impl DeviceFeatureOutput {
pub fn contains(&self, output_type: OutputType) -> bool {
match output_type {
OutputType::Constrict => self.constrict.is_some(),
OutputType::Temperature => self.temperature.is_some(),
OutputType::Led => self.led.is_some(),
OutputType::Oscillate => self.oscillate.is_some(),
OutputType::Position => self.position.is_some(),
OutputType::HwPositionWithDuration => self.hw_position_with_duration.is_some(),
OutputType::Rotate => self.rotate.is_some(),
OutputType::Spray => self.spray.is_some(),
OutputType::Unknown => false,
OutputType::Vibrate => self.vibrate.is_some(),
}
}
pub fn get(&self, output_type: OutputType) -> Option<&dyn DeviceFeatureOutputLimits> {
match output_type {
OutputType::Constrict => as_output_limits!(self.constrict()),
OutputType::Temperature => as_output_limits!(self.temperature()),
OutputType::Led => as_output_limits!(self.led()),
OutputType::Oscillate => as_output_limits!(self.oscillate()),
OutputType::Position => as_output_limits!(self.position()),
OutputType::HwPositionWithDuration => as_output_limits!(self.hw_position_with_duration()),
OutputType::Rotate => as_output_limits!(self.rotate()),
OutputType::Spray => as_output_limits!(self.spray()),
OutputType::Unknown => None,
OutputType::Vibrate => as_output_limits!(self.vibrate()),
}
}
}
#[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)")]
#[serde(serialize_with = "range_sequence_serialize")]
value: Vec<RangeInclusive<i32>>,
#[getset(get = "pub")]
command: HashSet<InputCommandType>,
}
impl DeviceFeatureInputProperties {
pub fn new(
value: &Vec<RangeInclusive<i32>>,
sensor_commands: &HashSet<InputCommandType>,
) -> Self {
Self {
value: value.clone(),
command: sensor_commands.clone(),
}
}
}
#[derive(Clone, Debug, Getters, Setters, Default, Serialize, Deserialize, Builder)]
#[builder(setter(strip_option), default)]
#[getset(get = "pub")]
#[serde(rename_all = "PascalCase")]
pub struct DeviceFeatureInput {
#[serde(skip_serializing_if = "Option::is_none")]
battery: Option<DeviceFeatureInputProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
rssi: Option<DeviceFeatureInputProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
pressure: Option<DeviceFeatureInputProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
button: Option<DeviceFeatureInputProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
depth: Option<DeviceFeatureInputProperties>,
#[serde(skip_serializing_if = "Option::is_none")]
position: Option<DeviceFeatureInputProperties>,
}
impl DeviceFeatureInput {
pub fn contains(&self, input_type: InputType) -> bool {
match input_type {
InputType::Battery => self.battery.is_some(),
InputType::Rssi => self.rssi.is_some(),
InputType::Pressure => self.pressure.is_some(),
InputType::Button => self.button.is_some(),
InputType::Depth => self.depth.is_some(),
InputType::Position => self.position.is_some(),
InputType::Unknown => false,
}
}
pub fn get(&self, input_type: InputType) -> &Option<DeviceFeatureInputProperties> {
match input_type {
InputType::Battery => self.battery(),
InputType::Rssi => self.rssi(),
InputType::Pressure => self.pressure(),
InputType::Button => self.button(),
InputType::Depth => self.depth(),
InputType::Position => self.position(),
InputType::Unknown => &None,
}
}
}