use super::{ElementMetadata, ModelElement};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Characteristic {
pub metadata: ElementMetadata,
pub data_type: Option<String>,
pub kind: CharacteristicKind,
pub constraints: Vec<Constraint>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CharacteristicKind {
Trait,
Quantifiable {
unit: String,
},
Measurement {
unit: String,
},
Enumeration {
values: Vec<String>,
},
State {
values: Vec<String>,
default_value: Option<String>,
},
Duration {
unit: String,
},
Collection {
element_characteristic: Option<Box<Characteristic>>,
},
List {
element_characteristic: Option<Box<Characteristic>>,
},
Set {
element_characteristic: Option<Box<Characteristic>>,
},
SortedSet {
element_characteristic: Option<Box<Characteristic>>,
},
TimeSeries {
element_characteristic: Option<Box<Characteristic>>,
},
Code,
Either {
left: Box<Characteristic>,
right: Box<Characteristic>,
},
SingleEntity {
entity_type: String,
},
StructuredValue {
deconstruction_rule: String,
elements: Vec<String>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Constraint {
LanguageConstraint {
language_code: String,
},
LocaleConstraint {
locale_code: String,
},
RangeConstraint {
min_value: Option<String>,
max_value: Option<String>,
lower_bound_definition: BoundDefinition,
upper_bound_definition: BoundDefinition,
},
LengthConstraint {
min_value: Option<u64>,
max_value: Option<u64>,
},
RegularExpressionConstraint {
pattern: String,
},
EncodingConstraint {
encoding: String,
},
FixedPointConstraint {
integer: u32,
scale: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BoundDefinition {
Open,
AtLeast,
GreaterThan,
LessThan,
}
impl Characteristic {
pub fn new(urn: String, kind: CharacteristicKind) -> Self {
Self {
metadata: ElementMetadata::new(urn),
data_type: None,
kind,
constraints: Vec::new(),
}
}
pub fn with_data_type(mut self, data_type: String) -> Self {
self.data_type = Some(data_type);
self
}
pub fn with_constraint(mut self, constraint: Constraint) -> Self {
self.constraints.push(constraint);
self
}
pub fn kind(&self) -> &CharacteristicKind {
&self.kind
}
}
impl ModelElement for Characteristic {
fn urn(&self) -> &str {
&self.metadata.urn
}
fn metadata(&self) -> &ElementMetadata {
&self.metadata
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_characteristic_creation() {
let characteristic = Characteristic::new(
"urn:samm:org.example:1.0.0#TestCharacteristic".to_string(),
CharacteristicKind::Trait,
)
.with_data_type("xsd:string".to_string());
assert_eq!(characteristic.name(), "TestCharacteristic");
assert_eq!(characteristic.data_type, Some("xsd:string".to_string()));
}
#[test]
fn test_enumeration_characteristic() {
let characteristic = Characteristic::new(
"urn:samm:org.example:1.0.0#StatusEnum".to_string(),
CharacteristicKind::Enumeration {
values: vec!["Active".to_string(), "Inactive".to_string()],
},
);
assert_eq!(characteristic.name(), "StatusEnum");
matches!(
characteristic.kind(),
CharacteristicKind::Enumeration { .. }
);
}
#[test]
fn test_measurement_characteristic() {
let characteristic = Characteristic::new(
"urn:samm:org.example:1.0.0#Speed".to_string(),
CharacteristicKind::Measurement {
unit: "unit:kilometrePerHour".to_string(),
},
);
assert_eq!(characteristic.name(), "Speed");
matches!(
characteristic.kind(),
CharacteristicKind::Measurement { .. }
);
}
}