use crate::app::attr::{
AnyAttribute, AttrDataType, AttrItem, AttrProp, AttrSet, Attribute, OwnedAttribute, TypeError,
};
use crate::outstation::database::AttrDefError;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
struct Variation {
value: u8,
}
struct Reserved(u8);
impl From<Reserved> for AttrDefError {
fn from(value: Reserved) -> Self {
Self::ReservedVariation(value.0)
}
}
impl From<Reserved> for AttrError {
fn from(value: Reserved) -> Self {
Self::ReservedVariation(value.0)
}
}
impl Variation {
fn create(value: u8) -> Result<Self, Reserved> {
match value {
0 | 254 | 255 => Err(Reserved(value)),
_ => Ok(Self { value }),
}
}
fn can_be_written(self) -> bool {
use crate::app::attr::var;
std::matches!(
self.value,
var::USER_ASSIGNED_SECONDARY_OPERATOR_NAME
| var::USER_ASSIGNED_PRIMARY_OPERATOR_NAME
| var::USER_ASSIGNED_OWNER_NAME
| var::USER_ASSIGNED_DEVICE_NAME
| var::USER_ASSIGNED_ID
| var::USER_ASSIGNED_LOCATION
)
}
}
type VarMap = BTreeMap<Variation, (AttrProp, OwnedAttribute)>;
#[derive(Default)]
pub(crate) struct SetMap {
sets: BTreeMap<AttrSet, VarMap>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum AttrError {
AttrNotDefined(AttrSet, u8),
SetNotDefined(AttrSet),
BadType(TypeError),
ReservedVariation(u8),
NotWritable(AttrSet, u8),
}
impl std::fmt::Display for AttrError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::AttrNotDefined(set, x) => write!(
f,
"Attribute with set = {set:?} is not defined for variation {x}"
),
Self::SetNotDefined(x) => write!(f, "Attribute set not defined: {x:?}"),
Self::BadType(x) => write!(
f,
"The type {:?} does not match the expected type {:?}",
x.actual, x.expected
),
Self::ReservedVariation(x) => {
write!(f, "Reserved variation cannot be defined or written: {x}")
}
Self::NotWritable(set, var) => write!(
f,
"Attribute with set = {set:?} and var = {var} cannot be written"
),
}
}
}
impl From<TypeError> for AttrError {
fn from(value: TypeError) -> Self {
Self::BadType(value)
}
}
impl SetMap {
pub(crate) fn define(
&mut self,
prop: AttrProp,
attr: OwnedAttribute,
) -> Result<(), AttrDefError> {
let variation = Variation::create(attr.variation)?;
let _ = AnyAttribute::try_from(&attr.view())?;
if attr.set == AttrSet::Default && prop.is_writable() && !variation.can_be_written() {
return Err(AttrDefError::NotWritable(attr.set, attr.variation));
}
match self.sets.entry(attr.set) {
Entry::Occupied(mut e) => match e.get_mut().entry(variation) {
Entry::Occupied(_) => Err(AttrDefError::AlreadyDefined),
Entry::Vacant(x) => {
x.insert((prop, attr));
Ok(())
}
},
Entry::Vacant(e) => {
let mut new_set = BTreeMap::new();
new_set.insert(variation, (prop, attr));
e.insert(new_set);
Ok(())
}
}
}
fn same_type(expected: AttrDataType, actual: AttrDataType) -> Result<(), TypeError> {
if expected == actual {
Ok(())
} else {
Err(TypeError::new(expected, actual))
}
}
pub(crate) fn can_write(&mut self, attr: Attribute) -> Result<(), AttrError> {
self.maybe_write(attr, false)
}
pub(crate) fn write(&mut self, attr: Attribute) -> Result<(), AttrError> {
self.maybe_write(attr, true)
}
fn maybe_write(&mut self, attr: Attribute, commit: bool) -> Result<(), AttrError> {
let key = Variation::create(attr.variation)?;
match self.get_set_mut(attr.set)?.get_mut(&key) {
None => Err(AttrError::AttrNotDefined(attr.set, key.value)),
Some((prop, current)) => {
if prop.is_writable() {
Self::same_type(current.value.data_type(), attr.value.data_type())?;
if commit {
match attr.to_owned() {
None => Err(AttrError::NotWritable(attr.set, key.value)),
Some(mut attr) => {
std::mem::swap(&mut attr, current);
Ok(())
}
}
} else {
Ok(())
}
} else {
Err(AttrError::NotWritable(attr.set, key.value))
}
}
}
}
pub(crate) fn exists(&self, set: AttrSet, var: u8) -> bool {
self.get(set, var).is_ok()
}
pub(crate) fn get(&self, set: AttrSet, var: u8) -> Result<&OwnedAttribute, AttrError> {
let key = Variation::create(var)?;
match self.get_set(set)?.get(&key) {
None => Err(AttrError::AttrNotDefined(set, key.value)),
Some((_, attr)) => Ok(attr),
}
}
pub(crate) fn variations(&self, set: AttrSet) -> Option<impl Iterator<Item = AttrItem> + '_> {
self.sets.get(&set).map(|x| {
x.iter().map(|(k, (prop, _))| AttrItem {
variation: k.value,
properties: *prop,
})
})
}
pub(crate) fn sets(&self) -> impl Iterator<Item = AttrSet> + '_ {
self.sets.keys().copied()
}
fn get_set_mut(&mut self, set: AttrSet) -> Result<&mut VarMap, AttrError> {
match self.sets.get_mut(&set) {
None => Err(AttrError::SetNotDefined(set)),
Some(set) => Ok(set),
}
}
fn get_set(&self, set: AttrSet) -> Result<&VarMap, AttrError> {
match self.sets.get(&set) {
None => Err(AttrError::SetNotDefined(set)),
Some(set) => Ok(set),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::app::attr::*;
#[test]
fn can_iterate_over_defined_attributes() {
let mut map = SetMap::default();
let attr1 = StringAttr::UserAssignedLocation.with_value("Bend");
let attr2 = StringAttr::ConfigVersion.with_value("1.0.0");
map.define(AttrProp::writable(), attr1).unwrap();
map.define(AttrProp::default(), attr2).unwrap();
assert!(map.variations(AttrSet::new(1)).is_none());
let mut items = map.variations(AttrSet::Default).unwrap();
assert_eq!(
items.next().unwrap(),
AttrItem {
variation: StringAttr::ConfigVersion.variation(),
properties: AttrProp::default()
}
);
assert_eq!(
items.next().unwrap(),
AttrItem {
variation: StringAttr::UserAssignedLocation.variation(),
properties: AttrProp::writable()
}
);
assert!(items.next().is_none());
}
#[test]
fn cannot_define_attributes_with_wrong_types_in_default_set() {
let mut map = SetMap::default();
let attr = OwnedAttribute::new(
AttrSet::Default,
StringAttr::UserAssignedLocation.variation(),
OwnedAttrValue::SignedInt(42),
);
let err = map.define(AttrProp::writable(), attr).unwrap_err();
assert_eq!(
err,
AttrDefError::BadType(TypeError {
expected: AttrDataType::VisibleString,
actual: AttrDataType::SignedInt
})
);
}
#[test]
fn cannot_define_non_writable_attribute_as_writable() {
let mut map = SetMap::default();
let attr = OwnedAttribute::new(
AttrSet::Default,
StringAttr::DeviceSubsetAndConformance.variation(),
OwnedAttrValue::VisibleString("3:2010".into()),
);
let err = map.define(AttrProp::writable(), attr).unwrap_err();
assert_eq!(
err,
AttrDefError::NotWritable(
AttrSet::Default,
StringAttr::DeviceSubsetAndConformance.variation()
)
);
}
#[test]
fn cannot_write_attribute_defined_with_different_type() {
let mut map = SetMap::default();
map.define(
AttrProp::writable(),
StringAttr::UserAssignedLocation.with_value("Bend"),
)
.unwrap();
let attr = Attribute {
set: Default::default(),
variation: StringAttr::UserAssignedLocation.variation(),
value: AttrValue::SignedInt(42),
};
let err = map.write(attr).unwrap_err();
assert_eq!(
err,
AttrError::BadType(TypeError {
expected: AttrDataType::VisibleString,
actual: AttrDataType::SignedInt
})
);
}
}