use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Parameter {
pub name: String,
pub display_name: String,
pub description: String,
pub value: ParameterValue,
pub data_type: ParameterDataType,
pub unit: Option<String>,
pub minimum: Option<f64>,
pub maximum: Option<f64>,
pub step: Option<f64>,
pub default_value: Option<ParameterValue>,
pub expressions: Vec<ParameterExpression>,
pub constraints: Vec<ParameterConstraint>,
pub is_read_only: bool,
pub is_hidden: bool,
pub is_optional: bool,
pub category: String,
pub sub_category: String,
pub order: i32,
pub icon: String,
pub tooltip: String,
pub validation_rules: Vec<ValidationRule>,
pub linked_parameters: Vec<String>,
pub formula: Option<String>,
pub formula_value: Option<f64>,
}
impl Default for Parameter {
fn default() -> Self {
Self::new()
}
}
impl Parameter {
pub fn new() -> Self {
Self {
name: String::new(),
display_name: String::new(),
description: String::new(),
value: ParameterValue::None,
data_type: ParameterDataType::String,
unit: None,
minimum: None,
maximum: None,
step: None,
default_value: None,
expressions: Vec::new(),
constraints: Vec::new(),
is_read_only: false,
is_hidden: false,
is_optional: false,
category: String::new(),
sub_category: String::new(),
order: 0,
icon: String::new(),
tooltip: String::new(),
validation_rules: Vec::new(),
linked_parameters: Vec::new(),
formula: None,
formula_value: None,
}
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn with_display_name(mut self, display_name: &str) -> Self {
self.display_name = display_name.to_string();
self
}
pub fn with_value(mut self, value: ParameterValue) -> Self {
self.value = value.clone();
if self.default_value.is_none() {
self.default_value = Some(value);
}
self
}
pub fn with_type(mut self, data_type: ParameterDataType) -> Self {
self.data_type = data_type;
self
}
pub fn with_unit(mut self, unit: &str) -> Self {
self.unit = Some(unit.to_string());
self
}
pub fn with_range(mut self, min: f64, max: f64) -> Self {
self.minimum = Some(min);
self.maximum = Some(max);
self
}
pub fn with_step(mut self, step: f64) -> Self {
self.step = Some(step);
self
}
pub fn set_value(&mut self, value: ParameterValue) -> bool {
if self.is_read_only {
return false;
}
if !self.validate(&value) {
return false;
}
self.value = value;
true
}
pub fn validate(&self, value: &ParameterValue) -> bool {
if let Some(min) = self.minimum {
if value.to_f64() < min {
return false;
}
}
if let Some(max) = self.maximum {
if value.to_f64() > max {
return false;
}
}
for rule in &self.validation_rules {
if !rule.validate(value) {
return false;
}
}
true
}
pub fn get_f64(&self) -> Option<f64> {
self.value.to_f64()
}
pub fn set_f64(&mut self, value: f64) -> bool {
self.set_value(ParameterValue::Real(value))
}
pub fn get_string(&self) -> Option<&str> {
match &self.value {
ParameterValue::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn set_string(&mut self, value: &str) -> bool {
self.set_value(ParameterValue::String(value.to_string()))
}
pub fn get_bool(&self) -> Option<bool> {
match &self.value {
ParameterValue::Boolean(b) => Some(*b),
_ => None,
}
}
pub fn set_bool(&mut self, value: bool) -> bool {
self.set_value(ParameterValue::Boolean(value))
}
pub fn reset_to_default(&mut self) -> bool {
if let Some(default) = &self.default_value {
self.set_value(default.clone())
} else {
false
}
}
pub fn add_constraint(&mut self, constraint: ParameterConstraint) {
self.constraints.push(constraint);
}
pub fn add_validation_rule(&mut self, rule: ValidationRule) {
self.validation_rules.push(rule);
}
pub fn link_to(&mut self, param_name: &str) {
if !self.linked_parameters.contains(¶m_name.to_string()) {
self.linked_parameters.push(param_name.to_string());
}
}
pub fn set_formula(&mut self, formula: &str) {
self.formula = Some(formula.to_string());
self.evaluate_formula();
}
pub fn evaluate_formula(&mut self) {
if let Some(formula) = &self.formula {
self.formula_value = Some(parse_simple_expression(formula));
}
}
pub fn format_value(&self) -> String {
match &self.value {
ParameterValue::Real(r) => {
if let Some(unit) = &self.unit {
format!("{}{}", r, unit)
} else {
format!("{}", r)
}
}
ParameterValue::String(s) => s.clone(),
ParameterValue::Integer(i) => i.to_string(),
ParameterValue::Boolean(b) => b.to_string(),
ParameterValue::Point(p) => format!("({}, {}, {})", p.x, p.y, p.z),
ParameterValue::Angle(a) => format!("{:.2}°", a.to_degrees()),
ParameterValue::None => "None".to_string(),
}
}
}
fn parse_simple_expression(expr: &str) -> f64 {
let cleaned = expr.replace(" ", "");
if let Ok(val) = cleaned.parse::<f64>() {
return val;
}
let expr_lower = cleaned.to_lowercase();
if expr_lower == "pi" || expr_lower == "Ï€" {
return std::f64::consts::PI;
}
if expr_lower == "2*pi" || expr_lower == "2Ï€" {
return 2.0 * std::f64::consts::PI;
}
if expr_lower == "pi/2" {
return std::f64::consts::PI / 2.0;
}
if expr_lower == "pi/4" {
return std::f64::consts::PI / 4.0;
}
if expr_lower == "e" {
return std::f64::consts::E;
}
0.0
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ParameterValue {
None,
Real(f64),
Integer(i32),
String(String),
Boolean(bool),
Point(crate::geometry::Point),
Angle(f64),
}
impl Default for ParameterValue {
fn default() -> Self {
ParameterValue::None
}
}
impl ParameterValue {
pub fn to_f64(&self) -> f64 {
match self {
ParameterValue::Real(r) => *r,
ParameterValue::Integer(i) => *i as f64,
ParameterValue::Boolean(b) => if *b { 1.0 } else { 0.0 },
_ => 0.0,
}
}
pub fn to_string(&self) -> String {
match self {
ParameterValue::Real(r) => format!("{}", r),
ParameterValue::Integer(i) => format!("{}", i),
ParameterValue::String(s) => s.clone(),
ParameterValue::Boolean(b) => format!("{}", b),
ParameterValue::Point(p) => format!("({}, {}, {})", p.x, p.y, p.z),
ParameterValue::Angle(a) => format!("{}", a),
ParameterValue::None => String::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ParameterDataType {
None,
Real,
Integer,
String,
Boolean,
Point,
Angle,
Distance,
Area,
Volume,
AngleUnitless,
Reference,
}
impl Default for ParameterDataType {
fn default() -> Self {
ParameterDataType::String
}
}
impl ParameterDataType {
pub fn name(&self) -> &str {
match self {
ParameterDataType::None => "None",
ParameterDataType::Real => "Real",
ParameterDataType::Integer => "Integer",
ParameterDataType::String => "String",
ParameterDataType::Boolean => "Boolean",
ParameterDataType::Point => "Point",
ParameterDataType::Angle => "Angle",
ParameterDataType::Distance => "Distance",
ParameterDataType::Area => "Area",
ParameterDataType::Volume => "Volume",
ParameterDataType::AngleUnitless => "Angle (Unitless)",
ParameterDataType::Reference => "Reference",
}
}
pub fn default_value(&self) -> ParameterValue {
match self {
ParameterDataType::Real => ParameterValue::Real(0.0),
ParameterDataType::Integer => ParameterValue::Integer(0),
ParameterDataType::String => ParameterValue::String(String::new()),
ParameterDataType::Boolean => ParameterValue::Boolean(false),
ParameterDataType::Point => ParameterValue::Point(crate::geometry::Point::origin()),
ParameterDataType::Angle => ParameterValue::Angle(0.0),
ParameterDataType::Distance => ParameterValue::Real(0.0),
ParameterDataType::Area => ParameterValue::Real(0.0),
ParameterDataType::Volume => ParameterValue::Real(0.0),
ParameterDataType::AngleUnitless => ParameterValue::Real(1.0),
ParameterDataType::Reference => ParameterValue::None,
ParameterDataType::None => ParameterValue::None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ParameterExpression {
pub name: String,
pub expression: String,
pub value: f64,
pub is_valid: bool,
}
impl Default for ParameterExpression {
fn default() -> Self {
Self::new()
}
}
impl ParameterExpression {
pub fn new() -> Self {
Self {
name: String::new(),
expression: String::new(),
value: 0.0,
is_valid: true,
}
}
pub fn with_expression(mut self, expr: &str) -> Self {
self.expression = expr.to_string();
self.evaluate();
self
}
pub fn evaluate(&mut self) {
self.value = parse_simple_expression(&self.expression);
self.is_valid = self.value.is_finite();
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ParameterConstraint {
pub name: String,
pub constraint_type: ParameterConstraintType,
pub value: ParameterValue,
}
impl Default for ParameterConstraint {
fn default() -> Self {
Self::new()
}
}
impl ParameterConstraint {
pub fn new() -> Self {
Self {
name: String::new(),
constraint_type: ParameterConstraintType::Fixed,
value: ParameterValue::None,
}
}
pub fn with_type(mut self, constraint_type: ParameterConstraintType) -> Self {
self.constraint_type = constraint_type;
self
}
pub fn with_value(mut self, value: ParameterValue) -> Self {
self.value = value;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ParameterConstraintType {
Fixed,
List,
Range,
Expression,
}
impl Default for ParameterConstraintType {
fn default() -> Self {
ParameterConstraintType::Fixed
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidationRule {
pub name: String,
pub rule_type: ValidationRuleType,
pub message: String,
pub value: ParameterValue,
}
impl Default for ValidationRule {
fn default() -> Self {
Self::new()
}
}
impl ValidationRule {
pub fn new() -> Self {
Self {
name: String::new(),
rule_type: ValidationRuleType::Required,
message: String::new(),
value: ParameterValue::None,
}
}
pub fn with_type(mut self, rule_type: ValidationRuleType) -> Self {
self.rule_type = rule_type;
self
}
pub fn with_message(mut self, message: &str) -> Self {
self.message = message.to_string();
self
}
pub fn validate(&self, value: &ParameterValue) -> bool {
match self.rule_type {
ValidationRuleType::Required => {
!matches!(value, ParameterValue::None)
}
ValidationRuleType::MinLength => {
if let ParameterValue::String(s) = value {
s.len() >= self.value.to_f64() as usize
} else {
true
}
}
ValidationRuleType::MaxLength => {
if let ParameterValue::String(s) = value {
s.len() <= self.value.to_f64() as usize
} else {
true
}
}
ValidationRuleType::Pattern => {
if let ParameterValue::String(s) = value {
regex::Regex::new(&self.message).map(|r| r.is_match(s)).unwrap_or(false)
} else {
true
}
}
ValidationRuleType::Custom => true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ValidationRuleType {
Required,
MinLength,
MaxLength,
Pattern,
Custom,
}
impl Default for ValidationRuleType {
fn default() -> Self {
ValidationRuleType::Required
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterCategory {
pub name: String,
pub display_name: String,
pub description: String,
pub parent_category: Option<String>,
pub icon: String,
pub order: i32,
pub parameters: Vec<String>,
pub sub_categories: Vec<String>,
}
impl Default for ParameterCategory {
fn default() -> Self {
Self::new()
}
}
impl ParameterCategory {
pub fn new() -> Self {
Self {
name: String::new(),
display_name: String::new(),
description: String::new(),
parent_category: None,
icon: String::new(),
order: 0,
parameters: Vec::new(),
sub_categories: Vec::new(),
}
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn add_parameter(&mut self, param_name: &str) {
if !self.parameters.contains(¶m_name.to_string()) {
self.parameters.push(param_name.to_string());
}
}
pub fn remove_parameter(&mut self, param_name: &str) {
self.parameters.retain(|p| p != param_name);
}
pub fn add_sub_category(&mut self, category_name: &str) {
if !self.sub_categories.contains(&category_name.to_string()) {
self.sub_categories.push(category_name.to_string());
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterGroup {
pub name: String,
pub display_name: String,
pub description: String,
pub parameters: Vec<String>,
pub is_collapsible: bool,
pub is_expanded: bool,
pub order: i32,
}
impl Default for ParameterGroup {
fn default() -> Self {
Self::new()
}
}
impl ParameterGroup {
pub fn new() -> Self {
Self {
name: String::new(),
display_name: String::new(),
description: String::new(),
parameters: Vec::new(),
is_collapsible: true,
is_expanded: true,
order: 0,
}
}
pub fn with_name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
pub fn add_parameter(&mut self, param_name: &str) {
if !self.parameters.contains(¶m_name.to_string()) {
self.parameters.push(param_name.to_string());
}
}
pub fn parameter_count(&self) -> usize {
self.parameters.len()
}
}
#[derive(Debug, Clone)]
pub struct ParameterManager {
parameters: HashMap<String, Parameter>,
categories: HashMap<String, ParameterCategory>,
groups: HashMap<String, ParameterGroup>,
active_category: Option<String>,
active_group: Option<String>,
is_modified: bool,
modified_parameters: Vec<String>,
parameter_order: Vec<String>,
}
impl Default for ParameterManager {
fn default() -> Self {
Self::new()
}
}
impl ParameterManager {
pub fn new() -> Self {
Self {
parameters: HashMap::new(),
categories: HashMap::new(),
groups: HashMap::new(),
active_category: None,
active_group: None,
is_modified: false,
modified_parameters: Vec::new(),
parameter_order: Vec::new(),
}
}
pub fn add(&mut self, parameter: Parameter) {
self.parameters.insert(parameter.name.clone(), parameter);
self.parameter_order.push(parameter.name.clone());
}
pub fn create(&mut self, name: &str) -> &mut Parameter {
let parameter = Parameter::with_name(name);
self.parameters.insert(name.to_string(), parameter);
self.parameter_order.push(name.to_string());
self.parameters.get_mut(name).unwrap()
}
pub fn get(&self, name: &str) -> Option<&Parameter> {
self.parameters.get(name)
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut Parameter> {
self.parameters.get_mut(name)
}
pub fn remove(&mut self, name: &str) -> bool {
self.parameter_order.retain(|n| n != name);
self.parameters.remove(name).is_some()
}
pub fn rename(&mut self, old_name: &str, new_name: &str) -> bool {
if let Some(param) = self.parameters.remove(old_name) {
let mut new_param = param;
new_param.name = new_name.to_string();
self.parameters.insert(new_name.to_string(), new_param);
self.parameter_order.retain(|n| n != old_name);
self.parameter_order.push(new_name.to_string());
true
} else {
false
}
}
pub fn set_value(&mut self, name: &str, value: ParameterValue) -> bool {
if let Some(param) = self.parameters.get_mut(name) {
let result = param.set_value(value);
if result {
self.is_modified = true;
if !self.modified_parameters.contains(&name.to_string()) {
self.modified_parameters.push(name.to_string());
}
}
result
} else {
false
}
}
pub fn set_f64(&mut self, name: &str, value: f64) -> bool {
self.set_value(name, ParameterValue::Real(value))
}
pub fn set_string(&mut self, name: &str, value: &str) -> bool {
self.set_value(name, ParameterValue::String(value.to_string()))
}
pub fn get_f64(&self, name: &str) -> Option<f64> {
self.parameters.get(name).and_then(|p| p.get_f64())
}
pub fn get_string(&self, name: &str) -> Option<&str> {
self.parameters.get(name).and_then(|p| p.get_string())
}
pub fn parameter_count(&self) -> usize {
self.parameters.len()
}
pub fn parameter_names(&self) -> Vec<&str> {
self.parameter_order.iter().map(|s| s.as_str()).collect()
}
pub fn parameters_in_category(&self, category: &str) -> Vec<&Parameter> {
self.parameters.values()
.filter(|p| p.category == category)
.collect()
}
pub fn parameters_in_group(&self, group: &str) -> Vec<&Parameter> {
self.groups.get(group)
.map(|g| {
g.parameters.iter()
.filter_map(|name| self.parameters.get(name))
.collect()
})
.unwrap_or_default()
}
pub fn add_category(&mut self, category: ParameterCategory) {
self.categories.insert(category.name.clone(), category);
}
pub fn get_category(&self, name: &str) -> Option<&ParameterCategory> {
self.categories.get(name)
}
pub fn category_names(&self) -> Vec<&str> {
self.categories.keys().map(|s| s.as_str()).collect()
}
pub fn add_group(&mut self, group: ParameterGroup) {
self.groups.insert(group.name.clone(), group);
}
pub fn get_group(&self, name: &str) -> Option<&ParameterGroup> {
self.groups.get(name)
}
pub fn group_names(&self) -> Vec<&str> {
self.groups.keys().map(|s| s.as_str()).collect()
}
pub fn set_active_category(&mut self, category: Option<&str>) {
self.active_category = category.map(|s| s.to_string());
}
pub fn set_active_group(&mut self, group: Option<&str>) {
self.active_group = group.map(|s| s.to_string());
}
pub fn active_category(&self) -> Option<&str> {
self.active_category.as_deref()
}
pub fn active_group(&self) -> Option<&str> {
self.active_group.as_deref()
}
pub fn search(&self, query: &str) -> Vec<&Parameter> {
let query_lower = query.to_lowercase();
self.parameters.values()
.filter(|p| {
p.name.to_lowercase().contains(&query_lower)
|| p.display_name.to_lowercase().contains(&query_lower)
|| p.description.to_lowercase().contains(&query_lower)
})
.collect()
}
pub fn is_modified(&self) -> bool {
self.is_modified
}
pub fn clear_modified(&mut self) {
self.is_modified = false;
self.modified_parameters.clear();
}
pub fn modified_parameter_names(&self) -> Vec<&str> {
self.modified_parameters.iter().map(|s| s.as_str()).collect()
}
pub fn reset_all_to_defaults(&mut self) -> usize {
let mut reset_count = 0;
for param in self.parameters.values_mut() {
if param.reset_to_default() {
reset_count += 1;
}
}
reset_count
}
pub fn validate_all(&mut self) -> Vec<&str> {
let mut invalid = Vec::new();
for name in self.parameters.keys() {
if let Some(param) = self.parameters.get(name) {
if !param.validate(¶m.value) {
invalid.push(name.as_str());
}
}
}
invalid
}
pub fn export_to_json(&self) -> String {
serde_json::to_string_pretty(&self.parameters).unwrap_or_default()
}
pub fn import_from_json(&mut self, json: &str) -> Result<usize, String> {
let imported: HashMap<String, Parameter> = serde_json::from_str(json)
.map_err(|e| e.to_string())?;
let count = imported.len();
for (name, param) in imported {
self.parameters.insert(name, param);
}
Ok(count)
}
pub fn clear(&mut self) {
self.parameters.clear();
self.categories.clear();
self.groups.clear();
self.parameter_order.clear();
self.modified_parameters.clear();
self.active_category = None;
self.active_group = None;
self.is_modified = false;
}
pub fn duplicate(&mut self, source_name: &str, new_name: &str) -> bool {
if let Some(source) = self.parameters.get(source_name) {
let mut new_param = source.clone();
new_param.name = new_name.to_string();
self.parameters.insert(new_name.to_string(), new_param);
self.parameter_order.push(new_name.to_string());
true
} else {
false
}
}
pub fn get_statistics(&self) -> ParameterStatistics {
let mut category_counts = HashMap::new();
let mut type_counts = HashMap::new();
for param in self.parameters.values() {
*category_counts.entry(param.category.clone()).or_insert(0) += 1;
*type_counts.entry(param.data_type).or_insert(0) += 1;
}
ParameterStatistics {
total_parameters: self.parameters.len(),
categories: self.categories.len(),
groups: self.groups.len(),
modified: self.modified_parameters.len(),
category_counts,
type_counts,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterStatistics {
pub total_parameters: usize,
pub categories: usize,
pub groups: usize,
pub modified: usize,
pub category_counts: HashMap<String, usize>,
pub type_counts: HashMap<ParameterDataType, usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parameter_creation() {
let param = Parameter::new()
.with_name("Length")
.with_display_name("Length")
.with_value(ParameterValue::Real(100.0))
.with_unit("mm")
.with_range(0.0, 1000.0);
assert_eq!(param.name, "Length");
assert_eq!(param.data_type, ParameterDataType::String);
assert_eq!(param.unit, Some("mm".to_string()));
}
#[test]
fn test_parameter_value_operations() {
let mut param = Parameter::new()
.with_name("Length")
.with_type(ParameterDataType::Distance)
.with_value(ParameterValue::Real(100.0));
assert!(param.set_f64(200.0));
assert!((param.get_f64().unwrap() - 200.0).abs() < 1e-10);
assert!(!param.set_f64(-10.0));
}
#[test]
fn test_parameter_range_validation() {
let mut param = Parameter::new()
.with_name("Width")
.with_range(0.0, 100.0);
assert!(param.set_f64(50.0));
assert!(!param.set_f64(150.0));
}
#[test]
fn test_parameter_string_operations() {
let mut param = Parameter::new()
.with_name("Description")
.with_type(ParameterDataType::String);
assert!(param.set_string("Test"));
assert_eq!(param.get_string(), Some("Test"));
}
#[test]
fn test_parameter_reset_to_default() {
let mut param = Parameter::new()
.with_name("Length")
.with_value(ParameterValue::Real(100.0));
param.set_f64(200.0);
param.reset_to_default();
assert!((param.get_f64().unwrap() - 100.0).abs() < 1e-10);
}
#[test]
fn test_parameter_value_conversions() {
assert!((ParameterValue::Real(3.14).to_f64() - 3.14).abs() < 1e-10);
assert!((ParameterValue::Integer(42).to_f64() - 42.0).abs() < 1e-10);
assert!((ParameterValue::Boolean(true).to_f64() - 1.0).abs() < 1e-10);
assert!((ParameterValue::Boolean(false).to_f64() - 0.0).abs() < 1e-10);
}
#[test]
fn test_parameter_data_types() {
assert_eq!(ParameterDataType::Real.name(), "Real");
assert_eq!(ParameterDataType::Distance.name(), "Distance");
assert_eq!(ParameterDataType::Point.name(), "Point");
}
#[test]
fn test_parameter_expression() {
let mut expr = ParameterExpression::new().with_expression("PI");
assert!((expr.value - std::f64::consts::PI).abs() < 1e-10);
}
#[test]
fn test_parameter_category() {
let mut category = ParameterCategory::with_name("Geometry");
category.add_parameter("Length");
category.add_parameter("Width");
assert_eq!(category.parameters.len(), 2);
}
#[test]
fn test_parameter_group() {
let mut group = ParameterGroup::with_name("Dimensions");
group.add_parameter("Length");
group.add_parameter("Width");
assert_eq!(group.parameter_count(), 2);
}
#[test]
fn test_parameter_manager_creation() {
let manager = ParameterManager::new();
assert_eq!(manager.parameter_count(), 0);
}
#[test]
fn test_parameter_manager_add() {
let mut manager = ParameterManager::new();
manager.add(
Parameter::new()
.with_name("Length")
.with_value(ParameterValue::Real(100.0))
);
assert_eq!(manager.parameter_count(), 1);
assert!(manager.get("Length").is_some());
}
#[test]
fn test_parameter_manager_set_value() {
let mut manager = ParameterManager::new();
manager.add(
Parameter::new()
.with_name("Length")
.with_value(ParameterValue::Real(100.0))
);
assert!(manager.set_f64("Length", 200.0));
assert!((manager.get_f64("Length").unwrap() - 200.0).abs() < 1e-10);
}
#[test]
fn test_parameter_manager_remove() {
let mut manager = ParameterManager::new();
manager.add(Parameter::with_name("Length"));
assert_eq!(manager.parameter_count(), 1);
assert!(manager.remove("Length"));
assert_eq!(manager.parameter_count(), 0);
}
#[test]
fn test_parameter_manager_rename() {
let mut manager = ParameterManager::new();
manager.add(Parameter::with_name("Length"));
assert!(manager.rename("Length", "Width"));
assert!(manager.get("Width").is_some());
assert!(manager.get("Length").is_none());
}
#[test]
fn test_parameter_manager_search() {
let mut manager = ParameterManager::new();
manager.add(Parameter::new().with_name("Length").with_display_name("Length of beam"));
manager.add(Parameter::new().with_name("Width").with_display_name("Width of beam"));
let results = manager.search("beam");
assert_eq!(results.len(), 2);
let results = manager.search("beam len");
assert_eq!(results.len(), 1);
}
#[test]
fn test_parameter_manager_reset_all() {
let mut manager = ParameterManager::new();
manager.add(Parameter::new().with_name("P1").with_value(ParameterValue::Real(100.0)));
manager.add(Parameter::new().with_name("P2").with_value(ParameterValue::Real(200.0)));
manager.set_f64("P1", 150.0);
manager.set_f64("P2", 250.0);
let reset_count = manager.reset_all_to_defaults();
assert_eq!(reset_count, 2);
}
#[test]
fn test_parameter_manager_validate_all() {
let mut manager = ParameterManager::new();
let mut param = Parameter::new().with_name("Length").with_range(0.0, 100.0);
param.set_f64(50.0);
manager.add(param);
let invalid = manager.validate_all();
assert_eq!(invalid.len(), 0);
}
#[test]
fn test_parameter_manager_export_import() {
let mut manager = ParameterManager::new();
manager.add(Parameter::new().with_name("Length").with_value(ParameterValue::Real(100.0)));
let json = manager.export_to_json();
assert!(json.contains("Length"));
let mut manager2 = ParameterManager::new();
manager2.import_from_json(&json).unwrap();
assert_eq!(manager2.parameter_count(), 1);
}
#[test]
fn test_parameter_manager_duplicate() {
let mut manager = ParameterManager::new();
manager.add(Parameter::new().with_name("Length").with_value(ParameterValue::Real(100.0)));
assert!(manager.duplicate("Length", "Width"));
assert_eq!(manager.parameter_count(), 2);
}
#[test]
fn test_parameter_manager_statistics() {
let mut manager = ParameterManager::new();
manager.add(Parameter::new().with_name("Length").with_value(ParameterValue::Real(100.0)));
manager.add(Parameter::new().with_name("Width").with_value(ParameterValue::Real(50.0)));
let stats = manager.get_statistics();
assert_eq!(stats.total_parameters, 2);
}
#[test]
fn test_validation_rule() {
let mut rule = ValidationRule::new()
.with_type(ValidationRuleType::MinLength)
.with_message("3");
assert!(rule.validate(&ParameterValue::String("Hello".to_string())));
assert!(!rule.validate(&ParameterValue::String("Hi".to_string())));
}
#[test]
fn test_parameter_linked() {
let mut param = Parameter::new().with_name("Width");
param.link_to("Length");
assert_eq!(param.linked_parameters.len(), 1);
}
#[test]
fn test_parameter_format_value() {
let mut param = Parameter::new()
.with_name("Length")
.with_unit("mm");
param.set_f64(100.0);
assert_eq!(param.format_value(), "100mm");
}
#[test]
fn test_parameter_constraint() {
let param = ParameterConstraint::new()
.with_type(ParameterConstraintType::Range)
.with_value(ParameterValue::Real(50.0));
assert_eq!(param.constraint_type, ParameterConstraintType::Range);
}
}