1use crate::KernelResult;
9
10pub trait ConstraintChannel: Send + Sync {
15 fn name(&self) -> &str;
17
18 fn evaluate(&self, state: &[f64]) -> KernelResult<f64>;
22
23 fn dimension_names(&self) -> Vec<String>;
25}
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30
31 struct FixedChannel {
32 name: String,
33 margin: f64,
34 dims: Vec<String>,
35 }
36
37 impl ConstraintChannel for FixedChannel {
38 fn name(&self) -> &str {
39 &self.name
40 }
41
42 fn evaluate(&self, _state: &[f64]) -> KernelResult<f64> {
43 Ok(self.margin)
44 }
45
46 fn dimension_names(&self) -> Vec<String> {
47 self.dims.clone()
48 }
49 }
50
51 #[test]
52 fn fixed_channel_returns_margin() {
53 let ch = FixedChannel {
54 name: "test".into(),
55 margin: 0.75,
56 dims: vec!["x".into()],
57 };
58 assert_eq!(ch.name(), "test");
59 assert!((ch.evaluate(&[1.0]).unwrap() - 0.75).abs() < f64::EPSILON);
60 assert_eq!(ch.dimension_names(), vec!["x".to_string()]);
61 }
62
63 #[test]
64 fn channel_is_object_safe() {
65 let ch: Box<dyn ConstraintChannel> = Box::new(FixedChannel {
66 name: "boxed".into(),
67 margin: 0.5,
68 dims: vec![],
69 });
70 assert_eq!(ch.name(), "boxed");
71 assert!((ch.evaluate(&[]).unwrap() - 0.5).abs() < f64::EPSILON);
72 }
73}