use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum ValueDto {
String(String),
Integer(i64),
Real(f64),
Boolean(bool),
Uninstantiated,
Reference(String),
List(Vec<ValueDto>),
FuzzyScalar(FuzzyScalar),
FuzzyNumber(FuzzyNumber),
Set(SetValue),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FuzzyScalar {
pub value: f64,
pub membership: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FuzzyNumber {
pub shape: FuzzyShapeDto,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SetValue {
pub lower: Vec<String>,
pub upper: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sort_constraint: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum FuzzyShapeDto {
Triangular { a: f64, b: f64, c: f64 },
Trapezoidal { a: f64, b: f64, c: f64, d: f64 },
Gaussian { mean: f64, std_dev: f64 },
CyclicGaussian {
mean: f64,
std_dev: f64,
period: f64,
},
Sigmoid { midpoint: f64, steepness: f64 },
Bell { center: f64, width: f64, slope: f64 },
SigmoidDifference {
midpoint1: f64,
steepness1: f64,
midpoint2: f64,
steepness2: f64,
},
GaussianProduct {
mean1: f64,
std_dev1: f64,
mean2: f64,
std_dev2: f64,
},
SigmoidProduct {
midpoint1: f64,
steepness1: f64,
midpoint2: f64,
steepness2: f64,
},
Cosine { center: f64, width: f64 },
Spike { center: f64, width: f64 },
Cauchy { center: f64, gamma: f64 },
SShape { a: f64, b: f64 },
ZShape { a: f64, b: f64 },
PiShape { a: f64, b: f64, c: f64, d: f64 },
PiecewiseLinear { points: Vec<(f64, f64)> },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum TaggedFeatureValueDto {
String(String),
Integer(i64),
Real(f64),
Boolean(bool),
Reference(String),
List(Vec<TaggedFeatureValueDto>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum FeatureTargetDto {
Value(TaggedFeatureValueDto),
Path(String),
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{json, Value};
#[test]
fn string_value_serializes_tagged() {
let v = ValueDto::String("hello".into());
assert_eq!(
serde_json::to_value(&v).unwrap(),
json!({"type": "String", "value": "hello"})
);
}
#[test]
fn integer_value_serializes_tagged() {
let v = ValueDto::Integer(42);
assert_eq!(
serde_json::to_value(&v).unwrap(),
json!({"type": "Integer", "value": 42})
);
}
#[test]
fn uninstantiated_serializes_without_value() {
let v = ValueDto::Uninstantiated;
let j: Value = serde_json::to_value(&v).unwrap();
assert_eq!(j, json!({"type": "Uninstantiated"}));
}
#[test]
fn real_value_roundtrip() {
let v = ValueDto::Real(2.5);
let j = serde_json::to_string(&v).unwrap();
let back: ValueDto = serde_json::from_str(&j).unwrap();
assert_eq!(back, v);
}
#[test]
fn list_value_roundtrip() {
let v = ValueDto::List(vec![ValueDto::String("a".into()), ValueDto::Integer(1)]);
let j = serde_json::to_string(&v).unwrap();
let back: ValueDto = serde_json::from_str(&j).unwrap();
assert_eq!(back, v);
}
#[test]
fn fuzzy_shape_uses_kind_tag() {
let v = FuzzyShapeDto::Triangular {
a: 20.0,
b: 22.0,
c: 24.0,
};
let j = serde_json::to_value(&v).unwrap();
assert_eq!(
j,
json!({"kind": "Triangular", "a": 20.0, "b": 22.0, "c": 24.0})
);
}
#[test]
fn gaussian_uses_snake_case_std_dev() {
let v = FuzzyShapeDto::Gaussian {
mean: 100.0,
std_dev: 15.0,
};
let j = serde_json::to_value(&v).unwrap();
assert_eq!(
j,
json!({"kind": "Gaussian", "mean": 100.0, "std_dev": 15.0})
);
}
#[test]
fn set_value_roundtrip() {
let v = ValueDto::Set(SetValue {
lower: vec!["a".into()],
upper: vec!["a".into(), "b".into()],
sort_constraint: Some("sort-1".into()),
});
let j = serde_json::to_string(&v).unwrap();
let back: ValueDto = serde_json::from_str(&j).unwrap();
assert_eq!(back, v);
}
}