solverforge_core/domain/
annotations.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(tag = "type")]
5pub enum PlanningAnnotation {
6    PlanningId,
7    PlanningEntity,
8    PlanningSolution,
9    PlanningVariable {
10        #[serde(default)]
11        value_range_provider_refs: Vec<String>,
12        #[serde(default)]
13        allows_unassigned: bool,
14    },
15    PlanningListVariable {
16        #[serde(default)]
17        value_range_provider_refs: Vec<String>,
18    },
19    PlanningScore {
20        #[serde(default)]
21        bendable_hard_levels: Option<usize>,
22        #[serde(default)]
23        bendable_soft_levels: Option<usize>,
24    },
25    ValueRangeProvider {
26        #[serde(default)]
27        id: Option<String>,
28    },
29    ProblemFactProperty,
30    ProblemFactCollectionProperty,
31    PlanningEntityProperty,
32    PlanningEntityCollectionProperty,
33    PlanningPin,
34    InverseRelationShadowVariable {
35        source_variable_name: String,
36    },
37}
38
39impl PlanningAnnotation {
40    pub fn planning_variable(value_range_provider_refs: Vec<String>) -> Self {
41        PlanningAnnotation::PlanningVariable {
42            value_range_provider_refs,
43            allows_unassigned: false,
44        }
45    }
46
47    pub fn planning_variable_unassigned(value_range_provider_refs: Vec<String>) -> Self {
48        PlanningAnnotation::PlanningVariable {
49            value_range_provider_refs,
50            allows_unassigned: true,
51        }
52    }
53
54    pub fn planning_list_variable(value_range_provider_refs: Vec<String>) -> Self {
55        PlanningAnnotation::PlanningListVariable {
56            value_range_provider_refs,
57        }
58    }
59
60    pub fn planning_score() -> Self {
61        PlanningAnnotation::PlanningScore {
62            bendable_hard_levels: None,
63            bendable_soft_levels: None,
64        }
65    }
66
67    pub fn planning_score_bendable(hard_levels: usize, soft_levels: usize) -> Self {
68        PlanningAnnotation::PlanningScore {
69            bendable_hard_levels: Some(hard_levels),
70            bendable_soft_levels: Some(soft_levels),
71        }
72    }
73
74    pub fn value_range_provider(id: impl Into<String>) -> Self {
75        PlanningAnnotation::ValueRangeProvider {
76            id: Some(id.into()),
77        }
78    }
79
80    pub fn inverse_relation_shadow(source_variable_name: impl Into<String>) -> Self {
81        PlanningAnnotation::InverseRelationShadowVariable {
82            source_variable_name: source_variable_name.into(),
83        }
84    }
85
86    pub fn is_planning_variable(&self) -> bool {
87        matches!(self, PlanningAnnotation::PlanningVariable { .. })
88    }
89
90    pub fn is_planning_list_variable(&self) -> bool {
91        matches!(self, PlanningAnnotation::PlanningListVariable { .. })
92    }
93
94    pub fn is_any_variable(&self) -> bool {
95        self.is_planning_variable() || self.is_planning_list_variable()
96    }
97
98    pub fn is_shadow_variable(&self) -> bool {
99        matches!(
100            self,
101            PlanningAnnotation::InverseRelationShadowVariable { .. }
102        )
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_planning_id() {
112        let ann = PlanningAnnotation::PlanningId;
113        assert_eq!(ann, PlanningAnnotation::PlanningId);
114    }
115
116    #[test]
117    fn test_planning_variable() {
118        let ann = PlanningAnnotation::planning_variable(vec!["rooms".to_string()]);
119        match ann {
120            PlanningAnnotation::PlanningVariable {
121                value_range_provider_refs,
122                allows_unassigned,
123            } => {
124                assert_eq!(value_range_provider_refs, vec!["rooms"]);
125                assert!(!allows_unassigned);
126            }
127            _ => panic!("Expected PlanningVariable"),
128        }
129    }
130
131    #[test]
132    fn test_planning_variable_unassigned() {
133        let ann = PlanningAnnotation::planning_variable_unassigned(vec!["slots".to_string()]);
134        match ann {
135            PlanningAnnotation::PlanningVariable {
136                value_range_provider_refs,
137                allows_unassigned,
138            } => {
139                assert_eq!(value_range_provider_refs, vec!["slots"]);
140                assert!(allows_unassigned);
141            }
142            _ => panic!("Expected PlanningVariable"),
143        }
144    }
145
146    #[test]
147    fn test_planning_list_variable() {
148        let ann = PlanningAnnotation::planning_list_variable(vec!["tasks".to_string()]);
149        match ann {
150            PlanningAnnotation::PlanningListVariable {
151                value_range_provider_refs,
152            } => {
153                assert_eq!(value_range_provider_refs, vec!["tasks"]);
154            }
155            _ => panic!("Expected PlanningListVariable"),
156        }
157    }
158
159    #[test]
160    fn test_planning_score() {
161        let ann = PlanningAnnotation::planning_score();
162        match ann {
163            PlanningAnnotation::PlanningScore {
164                bendable_hard_levels,
165                bendable_soft_levels,
166            } => {
167                assert!(bendable_hard_levels.is_none());
168                assert!(bendable_soft_levels.is_none());
169            }
170            _ => panic!("Expected PlanningScore"),
171        }
172    }
173
174    #[test]
175    fn test_planning_score_bendable() {
176        let ann = PlanningAnnotation::planning_score_bendable(2, 3);
177        match ann {
178            PlanningAnnotation::PlanningScore {
179                bendable_hard_levels,
180                bendable_soft_levels,
181            } => {
182                assert_eq!(bendable_hard_levels, Some(2));
183                assert_eq!(bendable_soft_levels, Some(3));
184            }
185            _ => panic!("Expected PlanningScore"),
186        }
187    }
188
189    #[test]
190    fn test_value_range_provider() {
191        let ann = PlanningAnnotation::value_range_provider("timeslots");
192        match ann {
193            PlanningAnnotation::ValueRangeProvider { id } => {
194                assert_eq!(id, Some("timeslots".to_string()));
195            }
196            _ => panic!("Expected ValueRangeProvider"),
197        }
198    }
199
200    #[test]
201    fn test_inverse_relation_shadow() {
202        let ann = PlanningAnnotation::inverse_relation_shadow("visits");
203        match ann {
204            PlanningAnnotation::InverseRelationShadowVariable {
205                source_variable_name,
206            } => {
207                assert_eq!(source_variable_name, "visits");
208            }
209            _ => panic!("Expected InverseRelationShadowVariable"),
210        }
211    }
212
213    #[test]
214    fn test_is_planning_variable() {
215        let var = PlanningAnnotation::planning_variable(vec![]);
216        assert!(var.is_planning_variable());
217        assert!(var.is_any_variable());
218        assert!(!var.is_planning_list_variable());
219        assert!(!var.is_shadow_variable());
220    }
221
222    #[test]
223    fn test_is_planning_list_variable() {
224        let var = PlanningAnnotation::planning_list_variable(vec![]);
225        assert!(!var.is_planning_variable());
226        assert!(var.is_any_variable());
227        assert!(var.is_planning_list_variable());
228        assert!(!var.is_shadow_variable());
229    }
230
231    #[test]
232    fn test_is_shadow_variable() {
233        let shadow = PlanningAnnotation::inverse_relation_shadow("test");
234        assert!(shadow.is_shadow_variable());
235        assert!(!shadow.is_any_variable());
236    }
237
238    #[test]
239    fn test_json_serialization_planning_id() {
240        let ann = PlanningAnnotation::PlanningId;
241        let json = serde_json::to_string(&ann).unwrap();
242        assert!(json.contains("\"type\":\"PlanningId\""));
243
244        let parsed: PlanningAnnotation = serde_json::from_str(&json).unwrap();
245        assert_eq!(parsed, ann);
246    }
247
248    #[test]
249    fn test_json_serialization_planning_variable() {
250        let ann =
251            PlanningAnnotation::planning_variable(vec!["rooms".to_string(), "slots".to_string()]);
252        let json = serde_json::to_string(&ann).unwrap();
253        assert!(json.contains("\"type\":\"PlanningVariable\""));
254        assert!(json.contains("\"value_range_provider_refs\""));
255
256        let parsed: PlanningAnnotation = serde_json::from_str(&json).unwrap();
257        assert_eq!(parsed, ann);
258    }
259
260    #[test]
261    fn test_json_serialization_planning_score_bendable() {
262        let ann = PlanningAnnotation::planning_score_bendable(2, 3);
263        let json = serde_json::to_string(&ann).unwrap();
264        assert!(json.contains("\"bendable_hard_levels\":2"));
265        assert!(json.contains("\"bendable_soft_levels\":3"));
266
267        let parsed: PlanningAnnotation = serde_json::from_str(&json).unwrap();
268        assert_eq!(parsed, ann);
269    }
270
271    #[test]
272    fn test_json_deserialization_defaults() {
273        let json = r#"{"type":"PlanningVariable"}"#;
274        let parsed: PlanningAnnotation = serde_json::from_str(json).unwrap();
275        match parsed {
276            PlanningAnnotation::PlanningVariable {
277                value_range_provider_refs,
278                allows_unassigned,
279            } => {
280                assert!(value_range_provider_refs.is_empty());
281                assert!(!allows_unassigned);
282            }
283            _ => panic!("Expected PlanningVariable"),
284        }
285    }
286
287    #[test]
288    fn test_simple_annotations() {
289        let annotations = vec![
290            PlanningAnnotation::PlanningEntity,
291            PlanningAnnotation::PlanningSolution,
292            PlanningAnnotation::ProblemFactProperty,
293            PlanningAnnotation::ProblemFactCollectionProperty,
294            PlanningAnnotation::PlanningEntityProperty,
295            PlanningAnnotation::PlanningEntityCollectionProperty,
296            PlanningAnnotation::PlanningPin,
297        ];
298
299        for ann in annotations {
300            let json = serde_json::to_string(&ann).unwrap();
301            let parsed: PlanningAnnotation = serde_json::from_str(&json).unwrap();
302            assert_eq!(parsed, ann);
303        }
304    }
305}