use crate::description::util::IterUtil;
use crate::validation::{ValidationError, ValidationErrorType, ValidationObject, ValidationResult};
use crate::values::{ColorCie, Name, Node, NodeExt};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AttributeDefinitions {
#[serde(
rename = "ActivationGroups",
skip_serializing_if = "Vec::is_empty",
serialize_with = "serialize_activation_groups",
deserialize_with = "deserialize_activation_groups",
default
)]
pub activation_groups: Vec<ActivationGroup>,
#[serde(
rename = "FeatureGroups",
skip_serializing_if = "Vec::is_empty",
serialize_with = "serialize_feature_groups",
deserialize_with = "deserialize_feature_groups"
)]
pub feature_groups: Vec<FeatureGroup>,
#[serde(
rename = "Attributes",
skip_serializing_if = "Vec::is_empty",
serialize_with = "serialize_attributes",
deserialize_with = "deserialize_attributes"
)]
pub attributes: Vec<Attribute>,
}
impl AttributeDefinitions {
pub fn activation_group(&self, name: &str) -> Option<&ActivationGroup> {
self.activation_groups
.iter()
.find(|group| group.name.as_ref().map(Name::as_ref) == Some(name))
}
pub fn feature_group(&self, name: &str) -> Option<&FeatureGroup> {
self.feature_groups
.iter()
.find(|group| group.name.as_ref().map(Name::as_ref) == Some(name))
}
pub fn attribute(&self, name: &str) -> Option<&Attribute> {
self.attributes
.iter()
.find(|attribute| attribute.name.as_ref().map(Name::as_ref) == Some(name))
}
pub fn validate(&self, result: &mut ValidationResult) {
let duplicate_activation_group_name = self
.activation_groups
.iter()
.filter_map(|group| group.name.as_ref())
.find_duplicate();
if let Some(name) = duplicate_activation_group_name {
result.errors.push(ValidationError::new(
ValidationObject::ActivationGroup,
name.to_string(),
ValidationErrorType::DuplicateName,
));
}
let duplicate_feature_group_name = self
.feature_groups
.iter()
.filter_map(|group| group.name.as_ref())
.find_duplicate();
if let Some(name) = duplicate_feature_group_name {
result.errors.push(ValidationError::new(
ValidationObject::FeatureGroup,
name.to_string(),
ValidationErrorType::DuplicateName,
));
}
let duplicate_attribute_name = self
.attributes
.iter()
.filter_map(|attribute| attribute.name.as_ref())
.find_duplicate();
if let Some(name) = duplicate_attribute_name {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
name.to_string(),
ValidationErrorType::DuplicateName,
));
}
for activation_group in &self.activation_groups {
activation_group.validate(result);
}
for feature_group in &self.feature_groups {
feature_group.validate(result);
}
for attribute in &self.attributes {
attribute.validate(self, result);
}
}
}
define_collect_helper!("ActivationGroup" (serialize_activation_groups, deserialize_activation_groups) -> ActivationGroup);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ActivationGroup {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
}
impl ActivationGroup {
pub fn validate(&self, result: &mut ValidationResult) {
if self.name.is_none() {
result.errors.push(ValidationError::new(
ValidationObject::ActivationGroup,
None,
ValidationErrorType::MissingName,
));
}
}
}
define_collect_helper!("FeatureGroup" (serialize_feature_groups, deserialize_feature_groups) -> FeatureGroup);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FeatureGroup {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Pretty")]
pub pretty: String,
#[serde(rename = "Feature", skip_serializing_if = "Vec::is_empty", default)]
pub features: Vec<Feature>,
}
impl FeatureGroup {
pub fn feature(&self, name: &str) -> Option<&Feature> {
self.features
.iter()
.find(|feature| feature.name.as_ref().map(|n| n.as_ref()) == Some(name))
}
pub fn validate(&self, result: &mut ValidationResult) {
if self.name.is_none() {
result.errors.push(ValidationError::new(
ValidationObject::FeatureGroup,
None,
ValidationErrorType::MissingName,
));
}
let duplicate_feature_name = self
.features
.iter()
.filter_map(|feature| feature.name.as_ref())
.find_duplicate();
if let Some(name) = duplicate_feature_name {
result.errors.push(ValidationError::new(
ValidationObject::Feature,
name.to_string(),
ValidationErrorType::DuplicateName,
));
}
for feature in &self.features {
feature.validate(result);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Feature {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
}
impl Feature {
pub fn validate(&self, result: &mut ValidationResult) {
if self.name.is_none() {
result.errors.push(ValidationError::new(
ValidationObject::Feature,
None,
ValidationErrorType::MissingName,
));
}
}
}
define_collect_helper!("Attribute" (serialize_attributes, deserialize_attributes) -> Attribute);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attribute {
#[serde(rename = "@Name", skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(rename = "@Pretty")]
pub pretty: String,
#[serde(rename = "@ActivationGroup", skip_serializing_if = "Option::is_none")]
pub activation_group: Option<Node>,
#[serde(rename = "@Feature")]
pub feature: Option<Node>,
#[serde(rename = "@MainAttribute", skip_serializing_if = "Option::is_none")]
pub main_attribute: Option<Node>,
#[serde(rename = "@PhysicalUnit")]
pub physical_unit: PhysicalUnit,
#[serde(rename = "@Color", skip_serializing_if = "Option::is_none")]
pub color: Option<ColorCie>,
#[serde(
rename = "SubPhysicalUnit",
skip_serializing_if = "Vec::is_empty",
default
)]
pub subphysical_units: Vec<SubPhysicalUnit>,
}
impl Attribute {
pub fn activation_group<'s>(
&self,
parent_attribute_definitions: &'s AttributeDefinitions,
) -> Option<&'s ActivationGroup> {
let name = self.activation_group.as_ref()?.single()?;
parent_attribute_definitions.activation_group(name)
}
pub fn feature<'s>(
&self,
parent_attribute_definitions: &'s AttributeDefinitions,
) -> Option<&'s Feature> {
let (group_name, tail) = self.feature.as_ref()?.split_first()?;
let feat_name = tail.single()?;
parent_attribute_definitions
.feature_group(group_name)?
.feature(feat_name)
}
pub fn main_attribute<'s>(
&self,
parent_attribute_definitions: &'s AttributeDefinitions,
) -> Option<&'s Attribute> {
let name = self.main_attribute.as_ref()?.single()?;
parent_attribute_definitions.attribute(name)
}
pub fn validate(
&self,
parent_attribute_definitions: &AttributeDefinitions,
result: &mut ValidationResult,
) {
if self.name.is_none() {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
None,
ValidationErrorType::MissingName,
));
}
let name = self.name.as_ref();
if let (Some(activation_group), None) = (
&self.activation_group,
self.activation_group(parent_attribute_definitions),
) {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
name.map(Name::to_string),
ValidationErrorType::LinkNotFound(
ValidationObject::ActivationGroup,
activation_group.clone(),
),
));
}
if let (Some(feature), None) = (&self.feature, self.feature(parent_attribute_definitions)) {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
name.map(Name::to_string),
ValidationErrorType::LinkNotFound(ValidationObject::Feature, feature.clone()),
));
}
if let (Some(main_attribute), None) = (
&self.main_attribute,
self.main_attribute(parent_attribute_definitions),
) {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
name.map(Name::to_string),
ValidationErrorType::LinkNotFound(
ValidationObject::Attribute,
main_attribute.clone(),
),
));
}
let duplicate_sub_physical_type = self
.subphysical_units
.iter()
.map(|unit| unit.type_)
.find_duplicate();
if let Some(sub_physical_unit_type) = duplicate_sub_physical_type {
result.errors.push(ValidationError::new(
ValidationObject::Attribute,
name.map(Name::to_string),
ValidationErrorType::DuplicateSubPhysicalUnit(sub_physical_unit_type),
));
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubPhysicalUnit {
#[serde(rename = "@Type")]
pub type_: SubPhysicalUnitType,
#[serde(rename = "@PhysicalUnit", default)]
pub physical_unit: PhysicalUnit,
#[serde(rename = "@PhysicalFrom", default = "physical_from_default")]
pub physical_from: f64,
#[serde(rename = "@PhysicalTo", default = "physical_to_default")]
pub physical_to: f64,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum SubPhysicalUnitType {
PlacementOffset,
Amplitude,
AmplitudeMin,
AmplitudeMax,
Duration,
DutyCycle,
TimeOffset,
MinimumOpening,
Value,
RatioHorizontal,
RatioVertical,
}
impl SubPhysicalUnitType {
pub fn as_str(self) -> &'static str {
match self {
SubPhysicalUnitType::PlacementOffset => "PlacementOffset",
SubPhysicalUnitType::Amplitude => "Amplitude",
SubPhysicalUnitType::AmplitudeMin => "AmplitudeMin",
SubPhysicalUnitType::AmplitudeMax => "AmplitudeMax",
SubPhysicalUnitType::Duration => "Duration",
SubPhysicalUnitType::DutyCycle => "DutyCycle",
SubPhysicalUnitType::TimeOffset => "TimeOffset",
SubPhysicalUnitType::MinimumOpening => "MinimumOpening",
SubPhysicalUnitType::Value => "Value",
SubPhysicalUnitType::RatioHorizontal => "RatioHorizontal",
SubPhysicalUnitType::RatioVertical => "RatioVertical",
}
}
}
impl Display for SubPhysicalUnitType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.serialize(f)
}
}
fn physical_from_default() -> f64 {
0.
}
fn physical_to_default() -> f64 {
1.
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum PhysicalUnit {
Percent,
Length,
Mass,
Time,
Temperature,
LuminousIntensity,
Angle,
Force,
Frequency,
Current,
Voltage,
Power,
Energy,
Area,
Volume,
Speed,
Acceleration,
AngularSpeed,
AngularAccc,
WaveLength,
ColorComponent,
#[default]
#[serde(other)]
None,
}