use crate::error::{Error, Result};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Value<T> {
Literal(T),
Parameter(String),
Expression(String),
}
impl<T> Value<T>
where
T: FromStr + Clone,
T::Err: std::fmt::Display,
{
pub fn resolve(&self, params: &HashMap<String, String>) -> Result<T> {
match self {
Value::Literal(value) => Ok(value.clone()),
Value::Parameter(param_name) => {
let param_value = params
.get(param_name)
.ok_or_else(|| Error::parameter_error(param_name, "parameter not found"))?;
param_value.parse::<T>().map_err(|e| {
Error::parameter_error(
param_name,
&format!("failed to parse '{}': {}", param_value, e),
)
})
}
Value::Expression(expr) => {
let resolved_expr = resolve_expression::<T>(expr, params)?;
resolved_expr.parse::<T>().map_err(|e| {
Error::parameter_error(
expr,
&format!(
"failed to parse expression result '{}': {}",
resolved_expr, e
),
)
})
}
}
}
#[inline]
pub fn as_literal(&self) -> Option<&T> {
match self {
Value::Literal(value) => Some(value),
Value::Parameter(_) => None,
Value::Expression(_) => None,
}
}
#[inline]
pub fn as_parameter(&self) -> Option<&str> {
match self {
Value::Literal(_) => None,
Value::Parameter(name) => Some(name),
Value::Expression(_) => None,
}
}
#[inline]
pub fn as_expression(&self) -> Option<&str> {
match self {
Value::Literal(_) => None,
Value::Parameter(_) => None,
Value::Expression(expr) => Some(expr),
}
}
}
impl<T: Clone> Value<T> {
#[inline]
pub fn literal(value: T) -> Self {
Value::Literal(value)
}
#[inline]
pub fn parameter(name: String) -> Self {
Value::Parameter(name)
}
#[inline]
pub fn expression(expr: String) -> Self {
Value::Expression(expr)
}
}
impl<'de, T> Deserialize<'de> for Value<T>
where
T: Deserialize<'de> + FromStr,
T::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.is_empty() && std::any::type_name::<T>().contains("f64") {
return Err(serde::de::Error::custom(
"Empty string is not a valid value for Double type",
));
}
if s.starts_with("${") && s.ends_with('}') && s.len() > 3 {
let content = &s[2..s.len() - 1];
if is_valid_parameter_name(content) && !content.contains(|c| "+-*/%()".contains(c)) {
Ok(Value::Parameter(content.to_string()))
} else {
Ok(Value::Expression(content.to_string()))
}
} else if s.starts_with("$") && s.len() > 1 {
let content = &s[1..];
if is_valid_parameter_name(content) && !content.contains(|c| "+-*/%()".contains(c)) {
Ok(Value::Parameter(content.to_string()))
} else {
match s.parse::<T>() {
Ok(value) => Ok(Value::Literal(value)),
Err(e) => Err(serde::de::Error::custom(format!(
"Failed to parse '{}': {}",
s, e
))),
}
}
} else {
match s.parse::<T>() {
Ok(value) => Ok(Value::Literal(value)),
Err(e) => Err(serde::de::Error::custom(format!(
"Failed to parse '{}': {}",
s, e
))),
}
}
}
}
impl<T> Serialize for Value<T>
where
T: Serialize + fmt::Display,
{
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Literal(value) => value.to_string().serialize(serializer),
Value::Parameter(name) => format!("${{{}}}", name).serialize(serializer),
Value::Expression(expr) => format!("${{{}}}", expr).serialize(serializer),
}
}
}
impl<T> fmt::Display for Value<T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Literal(value) => write!(f, "{}", value),
Value::Parameter(name) => write!(f, "${{{}}}", name),
Value::Expression(expr) => write!(f, "${{{}}}", expr),
}
}
}
pub type OSString = Value<String>;
pub type Double = Value<f64>;
pub type Int = Value<i32>;
pub type UnsignedInt = Value<u32>;
pub type UnsignedShort = Value<u16>;
pub type Boolean = Value<bool>;
pub type DateTime = Value<chrono::DateTime<chrono::Utc>>;
pub fn parse_parameter_reference(s: &str) -> Option<String> {
if s.starts_with("${") && s.ends_with('}') && s.len() > 3 {
let param_name = &s[2..s.len() - 1];
if is_valid_parameter_name(param_name) {
Some(param_name.to_string())
} else {
None
}
} else {
None
}
}
#[inline]
pub fn is_expression(s: &str) -> bool {
s.contains(|c| "+-*/%()".contains(c))
}
pub fn is_valid_parameter_name(name: &str) -> bool {
!name.is_empty()
&& name.chars().all(|c| c.is_alphanumeric() || c == '_')
&& !name.chars().next().unwrap().is_ascii_digit() }
fn resolve_expression<T: FromStr>(expr: &str, params: &HashMap<String, String>) -> Result<String>
where
T::Err: std::fmt::Display,
{
match crate::expression::evaluate_expression::<f64>(expr, params) {
Ok(result) => Ok(result.to_string()),
Err(_) => {
let mut result = expr.to_string();
for (param_name, param_value) in params {
let param_ref = format!("${{{}}}", param_name);
if result.contains(¶m_ref) {
result = result.replace(¶m_ref, param_value);
}
}
Ok(result)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use quick_xml;
#[test]
fn test_parameter_reference_parsing() {
assert_eq!(
parse_parameter_reference("${speed}"),
Some("speed".to_string())
);
assert_eq!(
parse_parameter_reference("${vehicle_speed}"),
Some("vehicle_speed".to_string())
);
assert_eq!(parse_parameter_reference("literal"), None);
assert_eq!(parse_parameter_reference("${}"), None);
assert_eq!(parse_parameter_reference("${123}"), None); }
#[test]
fn test_parameter_name_validation() {
assert!(is_valid_parameter_name("speed"));
assert!(is_valid_parameter_name("vehicle_speed"));
assert!(is_valid_parameter_name("speed123"));
assert!(!is_valid_parameter_name("123speed")); assert!(!is_valid_parameter_name("")); assert!(!is_valid_parameter_name("speed-limit")); }
#[test]
fn test_value_creation() {
let literal = Value::<f64>::literal(10.0);
assert!(matches!(literal, Value::Literal(10.0)));
let parameter = Value::<String>::parameter("speed".to_string());
assert!(matches!(parameter, Value::Parameter(_)));
let expression = Value::<String>::expression("speed + 10".to_string());
assert!(matches!(expression, Value::Expression(_)));
}
#[test]
fn test_value_resolution() {
let mut params = HashMap::new();
params.insert("speed".to_string(), "30.0".to_string());
params.insert("acceleration".to_string(), "2.5".to_string());
let literal = Value::<f64>::literal(10.0);
assert_eq!(literal.resolve(¶ms).unwrap(), 10.0);
let parameter = Value::<f64>::parameter("speed".to_string());
assert_eq!(parameter.resolve(¶ms).unwrap(), 30.0);
let expression = Value::<String>::expression("speed".to_string());
assert_eq!(expression.resolve(¶ms).unwrap(), "30");
}
#[test]
fn test_parameter_declaration_creation() {
let param = ParameterDeclaration::new(
"MaxSpeed".to_string(),
ParameterType::Double,
"60.0".to_string(),
);
assert_eq!(param.name.as_literal().unwrap(), "MaxSpeed");
assert_eq!(param.parameter_type, ParameterType::Double);
assert_eq!(param.value.as_literal().unwrap(), "60.0");
assert!(!param.has_constraints());
}
#[test]
fn test_parameter_declaration_with_constraints() {
let constraints = ValueConstraintGroup::new(vec![
ValueConstraint::greater_than("0.0".to_string()),
ValueConstraint::less_than("100.0".to_string()),
]);
let param = ParameterDeclaration::with_constraints(
"Speed".to_string(),
ParameterType::Double,
"30.0".to_string(),
vec![constraints],
);
assert!(param.has_constraints());
let constraint_group = ¶m.constraint_groups[0];
assert_eq!(constraint_group.value_constraints.len(), 2);
assert_eq!(
constraint_group.value_constraints[0].rule,
Rule::GreaterThan
);
assert_eq!(constraint_group.value_constraints[1].rule, Rule::LessThan);
}
#[test]
fn test_parameter_declaration_add_constraint() {
let mut param =
ParameterDeclaration::new("Age".to_string(), ParameterType::Int, "25".to_string());
assert!(!param.has_constraints());
param.add_constraint(ValueConstraint::greater_than("0".to_string()));
assert!(param.has_constraints());
param.add_constraint(ValueConstraint::less_than("120".to_string()));
let constraints = ¶m.constraint_groups[0];
assert_eq!(constraints.value_constraints.len(), 2);
}
#[test]
fn test_value_constraint_helpers() {
let eq_constraint = ValueConstraint::equal_to("test".to_string());
assert_eq!(eq_constraint.rule, Rule::EqualTo);
assert_eq!(eq_constraint.value.as_literal().unwrap(), "test");
let gt_constraint = ValueConstraint::greater_than("10".to_string());
assert_eq!(gt_constraint.rule, Rule::GreaterThan);
let lt_constraint = ValueConstraint::less_than("50".to_string());
assert_eq!(lt_constraint.rule, Rule::LessThan);
}
#[test]
fn test_range_creation() {
let range = Range::new(0.0, 100.0);
assert_eq!(range.lower_limit.as_literal().unwrap(), &0.0);
assert_eq!(range.upper_limit.as_literal().unwrap(), &100.0);
let default_range = Range::default();
assert_eq!(default_range.lower_limit.as_literal().unwrap(), &0.0);
assert_eq!(default_range.upper_limit.as_literal().unwrap(), &100.0);
}
#[test]
fn test_parameter_declarations_container() {
let mut declarations = ParameterDeclarations::default();
assert!(declarations.parameter_declarations.is_empty());
declarations
.parameter_declarations
.push(ParameterDeclaration::new(
"Speed".to_string(),
ParameterType::Double,
"30.0".to_string(),
));
declarations
.parameter_declarations
.push(ParameterDeclaration::new(
"VehicleName".to_string(),
ParameterType::String,
"Ego".to_string(),
));
assert_eq!(declarations.parameter_declarations.len(), 2);
assert_eq!(
declarations.parameter_declarations[0].parameter_type,
ParameterType::Double
);
assert_eq!(
declarations.parameter_declarations[1].parameter_type,
ParameterType::String
);
}
#[test]
fn test_directory_creation() {
let dir = Directory::new("/path/to/catalogs".to_string());
assert_eq!(dir.path.as_literal().unwrap(), "/path/to/catalogs");
let param_dir = Directory::from_parameter("CatalogPath".to_string());
assert_eq!(param_dir.path.as_parameter().unwrap(), "CatalogPath");
let default_dir = Directory::default();
assert_eq!(default_dir.path.as_literal().unwrap(), "");
}
#[test]
fn test_directory_path_resolution() {
let mut params = HashMap::new();
params.insert("CatalogPath".to_string(), "/catalogs/vehicles".to_string());
let dir = Directory::new("/path/to/catalogs".to_string());
assert_eq!(dir.resolve_path(¶ms).unwrap(), "/path/to/catalogs");
let param_dir = Directory::from_parameter("CatalogPath".to_string());
assert_eq!(
param_dir.resolve_path(¶ms).unwrap(),
"/catalogs/vehicles"
);
}
#[test]
fn test_directory_path_validation() {
let valid_dir = Directory::new("/path/to/catalogs".to_string());
assert!(valid_dir.validate_path());
let relative_dir = Directory::new("./catalogs".to_string());
assert!(relative_dir.validate_path());
let empty_dir = Directory::new("".to_string());
assert!(!empty_dir.validate_path());
let null_dir = Directory::new("path\0with\0null".to_string());
assert!(!null_dir.validate_path());
let param_dir = Directory::from_parameter("CatalogPath".to_string());
assert!(param_dir.validate_path());
}
#[test]
fn test_directory_serialization() {
let dir = Directory::new("/path/to/catalogs".to_string());
let json = serde_json::to_string(&dir).unwrap();
assert!(json.contains("path"));
assert!(json.contains("/path/to/catalogs"));
let deserialized: Directory = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.path.as_literal().unwrap(), "/path/to/catalogs");
}
#[test]
fn test_scientific_notation_parsing() {
let test_values = [
"0.0000000000000000e+00",
"1.5000000000000000e+00",
"9.2884257876425379e-04",
"3.7479999999999983e+01",
"-2.8099999999999987e+00",
];
for val in test_values {
println!("Testing: '{}'", val);
match val.parse::<f64>() {
Ok(f) => println!(" Direct f64::parse: {}", f),
Err(e) => println!(" Direct f64::parse ERROR: {}", e),
}
let json_str = format!("\"{}\"", val);
match serde_json::from_str::<Double>(&json_str) {
Ok(double_val) => {
println!(" Value<f64> JSON: {:?}", double_val);
if let Some(literal_val) = double_val.as_literal() {
println!(" Literal value: {}", literal_val);
}
}
Err(e) => println!(" Value<f64> JSON ERROR: {}", e),
}
let xml_str = format!("<test>{}</test>", val);
match quick_xml::de::from_str::<Double>(&xml_str) {
Ok(double_val) => {
println!(" Value<f64> XML: {:?}", double_val);
if let Some(literal_val) = double_val.as_literal() {
println!(" XML Literal value: {}", literal_val);
}
}
Err(e) => println!(" Value<f64> XML ERROR: {}", e),
}
println!();
}
}
#[test]
fn test_parameter_declaration_multiple_constraint_groups() {
let constraint_group1 =
ValueConstraintGroup::new(vec![ValueConstraint::equal_to("1".to_string())]);
let constraint_group2 =
ValueConstraintGroup::new(vec![ValueConstraint::equal_to("-1".to_string())]);
let param = ParameterDeclaration::with_constraints(
"SideVehicle_InitPosition_RelativeLaneId".to_string(),
ParameterType::Int,
"1".to_string(),
vec![constraint_group1, constraint_group2],
);
assert!(param.has_constraints());
assert_eq!(param.constraint_groups.len(), 2);
assert_eq!(param.constraint_groups[0].value_constraints.len(), 1);
assert_eq!(
param.constraint_groups[0].value_constraints[0].rule,
Rule::EqualTo
);
assert_eq!(
param.constraint_groups[0].value_constraints[0]
.value
.as_literal()
.unwrap(),
"1"
);
assert_eq!(param.constraint_groups[1].value_constraints.len(), 1);
assert_eq!(
param.constraint_groups[1].value_constraints[0].rule,
Rule::EqualTo
);
assert_eq!(
param.constraint_groups[1].value_constraints[0]
.value
.as_literal()
.unwrap(),
"-1"
);
}
#[test]
fn test_alks_scenario_constraint_pattern() {
let xml = r#"
<ParameterDeclaration name="SideVehicle_InitPosition_RelativeLaneId" parameterType="int" value="1">
<ConstraintGroup>
<ValueConstraint rule="equalTo" value="1"></ValueConstraint>
</ConstraintGroup>
<ConstraintGroup>
<ValueConstraint rule="equalTo" value="-1"></ValueConstraint>
</ConstraintGroup>
</ParameterDeclaration>
"#;
let result = quick_xml::de::from_str::<ParameterDeclaration>(xml);
assert!(
result.is_ok(),
"Failed to parse ALKS constraint pattern: {:?}",
result.err()
);
let param = result.unwrap();
assert_eq!(
param.name.as_literal().unwrap(),
"SideVehicle_InitPosition_RelativeLaneId"
);
assert_eq!(param.parameter_type, ParameterType::Int);
assert_eq!(param.value.as_literal().unwrap(), "1");
assert_eq!(param.constraint_groups.len(), 2);
}
#[test]
fn test_value_display_trait() {
let literal_value = Value::<f64>::literal(42.5);
assert_eq!(format!("{}", literal_value), "42.5");
let parameter_value = Value::<String>::parameter("speed".to_string());
assert_eq!(format!("{}", parameter_value), "${speed}");
let expression_value = Value::<String>::expression("speed * 2".to_string());
assert_eq!(format!("{}", expression_value), "${speed * 2}");
let bool_literal = Value::<bool>::literal(true);
assert_eq!(format!("{}", bool_literal), "true");
let string_literal = Value::<String>::literal("hello".to_string());
assert_eq!(format!("{}", string_literal), "hello");
let double_value = Double::literal(3.14);
assert_eq!(format!("{}", double_value), "3.14");
let os_string_param = OSString::parameter("vehicle_name".to_string());
assert_eq!(format!("{}", os_string_param), "${vehicle_name}");
let boolean_expr = Boolean::expression("speed > 30".to_string());
assert_eq!(format!("{}", boolean_expr), "${speed > 30}");
}
}
use crate::types::enums::{ParameterType, Rule};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct ParameterDeclarations {
#[serde(rename = "ParameterDeclaration", default)]
pub parameter_declarations: Vec<ParameterDeclaration>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParameterDeclaration {
#[serde(rename = "@name")]
pub name: OSString,
#[serde(rename = "@parameterType")]
pub parameter_type: ParameterType,
#[serde(rename = "@value")]
pub value: OSString,
#[serde(
rename = "ConstraintGroup",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub constraint_groups: Vec<ValueConstraintGroup>,
}
impl Default for ParameterDeclaration {
fn default() -> Self {
Self {
name: OSString::literal("DefaultParameter".to_string()),
parameter_type: ParameterType::String,
value: OSString::literal("".to_string()),
constraint_groups: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct ValueConstraintGroup {
#[serde(rename = "ValueConstraint")]
pub value_constraints: Vec<ValueConstraint>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ValueConstraint {
#[serde(rename = "@rule")]
pub rule: Rule,
#[serde(rename = "@value")]
pub value: OSString,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Range {
#[serde(rename = "@lowerLimit")]
pub lower_limit: Double,
#[serde(rename = "@upperLimit")]
pub upper_limit: Double,
}
impl Default for ValueConstraint {
fn default() -> Self {
Self {
rule: Rule::EqualTo,
value: OSString::literal("0".to_string()),
}
}
}
impl Default for Range {
fn default() -> Self {
Self {
lower_limit: Double::literal(0.0),
upper_limit: Double::literal(100.0),
}
}
}
impl ParameterDeclaration {
pub fn new(name: String, parameter_type: ParameterType, value: String) -> Self {
Self {
name: OSString::literal(name),
parameter_type,
value: OSString::literal(value),
constraint_groups: Vec::new(),
}
}
pub fn with_constraints(
name: String,
parameter_type: ParameterType,
value: String,
constraints: Vec<ValueConstraintGroup>,
) -> Self {
Self {
name: OSString::literal(name),
parameter_type,
value: OSString::literal(value),
constraint_groups: constraints,
}
}
pub fn add_constraint(&mut self, constraint: ValueConstraint) {
if let Some(group) = self.constraint_groups.last_mut() {
group.value_constraints.push(constraint);
} else {
self.constraint_groups.push(ValueConstraintGroup {
value_constraints: vec![constraint],
});
}
}
pub fn has_constraints(&self) -> bool {
!self.constraint_groups.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Directory {
#[serde(rename = "@path")]
pub path: OSString,
}
impl Directory {
pub fn new(path: String) -> Self {
Self {
path: OSString::literal(path),
}
}
pub fn from_parameter(param_name: String) -> Self {
Self {
path: OSString::parameter(param_name),
}
}
pub fn resolve_path(&self, params: &HashMap<String, String>) -> Result<String> {
self.path.resolve(params)
}
pub fn validate_path(&self) -> bool {
if let Some(literal_path) = self.path.as_literal() {
!literal_path.is_empty() && !literal_path.contains('\0')
} else {
true
}
}
}
impl Default for Directory {
fn default() -> Self {
Self::new(String::new())
}
}
impl ValueConstraintGroup {
pub fn new(constraints: Vec<ValueConstraint>) -> Self {
Self {
value_constraints: constraints,
}
}
pub fn add_constraint(&mut self, constraint: ValueConstraint) {
self.value_constraints.push(constraint);
}
}
impl ValueConstraint {
pub fn new(rule: Rule, value: String) -> Self {
Self {
rule,
value: OSString::literal(value),
}
}
pub fn equal_to(value: String) -> Self {
Self::new(Rule::EqualTo, value)
}
pub fn greater_than(value: String) -> Self {
Self::new(Rule::GreaterThan, value)
}
pub fn less_than(value: String) -> Self {
Self::new(Rule::LessThan, value)
}
}
impl Range {
pub fn new(lower: f64, upper: f64) -> Self {
debug_assert!(lower <= upper, "Range lower limit must be <= upper limit");
Self {
lower_limit: Double::literal(lower),
upper_limit: Double::literal(upper),
}
}
pub fn try_new(lower: f64, upper: f64) -> Result<Self> {
if lower > upper {
return Err(Error::validation_error(
"Range",
"Range lower limit must be <= upper limit",
));
}
Ok(Self {
lower_limit: Double::literal(lower),
upper_limit: Double::literal(upper),
})
}
}