use indexmap::IndexSet;
use std::ops::{Shl, Shr};
use try_from::TryFrom;
use {
DataType, HasDataType, ParameterMaskOutOfRange,
ParameterOutOfMaskRange, ParametersNotInParameterSet, Result,
};
#[cfg(feature = "bidir-map")]
use bidir_map::BidirMap;
pub trait IsParameterMask: Clone {}
impl IsParameterMask for IndexSet<String> {}
impl IsParameterMask for u64 {}
pub type DataValueRaw = DataValue<u64>;
pub type DataValueDescriptive = DataValue<IndexSet<String>>;
impl TryFrom<(DataType, u64)> for DataValueRaw {
type Err = std::io::Error;
fn try_from(value: (DataType, u64)) -> std::io::Result<Self> {
let (tag, data) = value;
match tag {
DataType::Boolean => Ok(DataValue::Boolean(data > 0u64)),
DataType::Int8 => Ok(DataValue::Int8(data as i8)),
DataType::Int16 => Ok(DataValue::Int16(data as i16)),
DataType::Int32 => Ok(DataValue::Int32(data as i32)),
DataType::Int64 => Ok(DataValue::Int64(data as i64)),
DataType::UInt8 => Ok(DataValue::UInt8(data as u8)),
DataType::UInt16 => Ok(DataValue::UInt16(data as u16)),
DataType::UInt32 => Ok(DataValue::UInt32(data as u32)),
DataType::UInt64 => Ok(DataValue::UInt64(data as u64)),
DataType::ParameterMask => {
Ok(DataValue::ParameterMask(data as u64))
}
DataType::Invalid => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid DataValue enum tag {}", tag),
)),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase", untagged))]
pub enum DataValue<M: IsParameterMask> {
Boolean(bool),
UInt8(u8),
UInt16(u16),
UInt32(u32),
UInt64(u64),
Int8(i8),
Int16(i16),
Int32(i32),
Int64(i64),
ParameterMask(M),
}
impl<T: IsParameterMask> From<bool> for DataValue<T> {
fn from(v: bool) -> DataValue<T> {
DataValue::Boolean(v)
}
}
impl<T: IsParameterMask> From<i8> for DataValue<T> {
fn from(v: i8) -> DataValue<T> {
DataValue::Int8(v)
}
}
impl<T: IsParameterMask> From<i16> for DataValue<T> {
fn from(v: i16) -> DataValue<T> {
DataValue::Int16(v)
}
}
impl<T: IsParameterMask> From<i32> for DataValue<T> {
fn from(v: i32) -> DataValue<T> {
DataValue::Int32(v)
}
}
impl<T: IsParameterMask> From<i64> for DataValue<T> {
fn from(v: i64) -> DataValue<T> {
DataValue::Int64(v)
}
}
impl<T: IsParameterMask> From<u8> for DataValue<T> {
fn from(v: u8) -> DataValue<T> {
DataValue::UInt8(v)
}
}
impl<T: IsParameterMask> From<u16> for DataValue<T> {
fn from(v: u16) -> DataValue<T> {
DataValue::UInt16(v)
}
}
impl<T: IsParameterMask> From<u32> for DataValue<T> {
fn from(v: u32) -> DataValue<T> {
DataValue::UInt32(v)
}
}
impl<T: IsParameterMask> From<u64> for DataValue<T> {
fn from(v: u64) -> DataValue<T> {
DataValue::UInt64(v)
}
}
impl DataValueRaw {
pub fn to_descriptive<P>(
&self,
parameters: P,
) -> Result<DataValueDescriptive>
where
u64: DeconstructParameterMask<P>,
{
use DataValue::*;
match *self {
Boolean(v) => Ok(Boolean(v)),
UInt8(v) => Ok(UInt8(v)),
UInt16(v) => Ok(UInt16(v)),
UInt32(v) => Ok(UInt32(v)),
UInt64(v) => Ok(UInt64(v)),
Int8(v) => Ok(Int8(v)),
Int16(v) => Ok(Int16(v)),
Int32(v) => Ok(Int32(v)),
Int64(v) => Ok(Int64(v)),
ParameterMask(v) => Ok(ParameterMask(
v.deconstruct_parameter_mask(parameters)?,
)),
}
}
}
impl DataValueDescriptive {
pub fn to_raw<'a, P>(&'a self, parameters: P) -> Result<DataValueRaw>
where
&'a IndexSet<String>: ConstructParameterMask<P>,
{
use DataValue::*;
match *self {
Boolean(ref v) => Ok(Boolean(*v)),
UInt8(ref v) => Ok(UInt8(*v)),
UInt16(ref v) => Ok(UInt16(*v)),
UInt32(ref v) => Ok(UInt32(*v)),
UInt64(ref v) => Ok(UInt64(*v)),
Int8(ref v) => Ok(Int8(*v)),
Int16(ref v) => Ok(Int16(*v)),
Int32(ref v) => Ok(Int32(*v)),
Int64(ref v) => Ok(Int64(*v)),
ParameterMask(ref v) => Ok(ParameterMask(
(&v).construct_parameter_mask(parameters)?,
)),
}
}
}
impl<M: IsParameterMask> HasDataType for DataValue<M> {
fn data_type(&self) -> DataType {
use DataValue::*;
match *self {
Boolean(_) => DataType::Boolean,
UInt8(_) => DataType::UInt8,
UInt16(_) => DataType::UInt16,
UInt32(_) => DataType::UInt32,
UInt64(_) => DataType::UInt64,
Int8(_) => DataType::Int8,
Int16(_) => DataType::Int16,
Int32(_) => DataType::Int32,
Int64(_) => DataType::Int64,
ParameterMask(_) => DataType::ParameterMask,
}
}
}
pub trait ConstructParameterMask<P> {
fn construct_parameter_mask(&self, parameters: P) -> Result<u64>;
}
impl<'a> ConstructParameterMask<&'a [String]> for &'a IndexSet<String> {
fn construct_parameter_mask(
&self,
parameters: &[String],
) -> Result<u64> {
(&self.iter().map(|v| v.to_string()).collect::<Vec<String>>()[..])
.construct_parameter_mask(parameters)
}
}
#[cfg(feature = "bidir-map")]
impl<'a> ConstructParameterMask<&'a BidirMap<String, u16>>
for &'a IndexSet<String>
{
fn construct_parameter_mask(
&self,
parameters: &BidirMap<String, u16>,
) -> Result<u64> {
let mut mask = 0u64;
let mut unused = IndexSet::new();
for parameter in self.iter() {
if let Some(i) = parameters.get_by_first(parameter) {
if *i >= 64u16 {
ParameterOutOfMaskRange {
parameter: parameter.to_string(),
index: (*i) as usize,
}
.fail()?;
}
mask |= 1u64.shl(i);
} else {
unused.insert(parameter.to_string());
}
}
if !unused.is_empty() {
ParametersNotInParameterSet { parameters: unused }.fail()?;
}
Ok(mask)
}
}
impl<'a> ConstructParameterMask<&'a [String]> for &'a [String] {
fn construct_parameter_mask(
&self,
parameters: &[String],
) -> Result<u64> {
let mut mask = 0u64;
let mut unused = IndexSet::new();
for (i, parameter) in self.iter().enumerate() {
if parameters.contains(¶meter) {
if i >= 64 {
ParameterOutOfMaskRange {
parameter: parameter.to_string(),
index: i,
}
.fail()?;
} else {
mask |= 1u64.shl(i);
}
} else {
unused.insert(parameter.to_string());
}
}
if !unused.is_empty() {
ParametersNotInParameterSet { parameters: unused }.fail()?;
}
Ok(mask)
}
}
pub trait DeconstructParameterMask<P> {
fn deconstruct_parameter_mask(
&self,
parameters: P,
) -> Result<IndexSet<String>>;
}
impl<'a> DeconstructParameterMask<&'a [String]> for u64 {
fn deconstruct_parameter_mask(
&self,
parameters: &[String],
) -> Result<IndexSet<String>> {
let mut mask = IndexSet::new();
for i in 0usize..64usize {
if self.shr(i) & 1u64 == 1u64 {
if let Some(p) = parameters.get(i) {
mask.insert(p.to_string());
} else {
ParameterMaskOutOfRange {
index: i,
mask: *self,
}
.fail()?;
}
}
}
Ok(mask)
}
}
#[cfg(feature = "bidir-map")]
impl<'a> DeconstructParameterMask<&'a BidirMap<String, u16>> for u64 {
fn deconstruct_parameter_mask(
&self,
parameters: &BidirMap<String, u16>,
) -> Result<IndexSet<String>> {
let mut mask = IndexSet::new();
for i in 0u16..64u16 {
if self.shr(i as usize) & 1u64 == 1u64 {
if let Some(p) = parameters.get_by_second(&i) {
mask.insert(p.to_string());
} else {
ParameterMaskOutOfRange {
index: i as usize,
mask: *self,
}
.fail()?;
}
}
}
Ok(mask)
}
}