solverforge_core/domain/
shadow.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(tag = "type")]
5pub enum ShadowAnnotation {
6    ShadowVariable {
7        source_variable_name: String,
8        #[serde(default)]
9        source_entity_class: Option<String>,
10    },
11    InverseRelationShadowVariable {
12        source_variable_name: String,
13    },
14    IndexShadowVariable {
15        source_variable_name: String,
16    },
17    PreviousElementShadowVariable {
18        source_variable_name: String,
19    },
20    NextElementShadowVariable {
21        source_variable_name: String,
22    },
23    AnchorShadowVariable {
24        source_variable_name: String,
25    },
26    PiggybackShadowVariable {
27        shadow_variable_name: String,
28    },
29    CascadingUpdateShadowVariable {
30        target_method_name: String,
31    },
32}
33
34impl ShadowAnnotation {
35    pub fn shadow_variable(source_variable_name: impl Into<String>) -> Self {
36        ShadowAnnotation::ShadowVariable {
37            source_variable_name: source_variable_name.into(),
38            source_entity_class: None,
39        }
40    }
41
42    pub fn shadow_variable_with_class(
43        source_variable_name: impl Into<String>,
44        source_entity_class: impl Into<String>,
45    ) -> Self {
46        ShadowAnnotation::ShadowVariable {
47            source_variable_name: source_variable_name.into(),
48            source_entity_class: Some(source_entity_class.into()),
49        }
50    }
51
52    pub fn inverse_relation(source_variable_name: impl Into<String>) -> Self {
53        ShadowAnnotation::InverseRelationShadowVariable {
54            source_variable_name: source_variable_name.into(),
55        }
56    }
57
58    pub fn index(source_variable_name: impl Into<String>) -> Self {
59        ShadowAnnotation::IndexShadowVariable {
60            source_variable_name: source_variable_name.into(),
61        }
62    }
63
64    pub fn previous_element(source_variable_name: impl Into<String>) -> Self {
65        ShadowAnnotation::PreviousElementShadowVariable {
66            source_variable_name: source_variable_name.into(),
67        }
68    }
69
70    pub fn next_element(source_variable_name: impl Into<String>) -> Self {
71        ShadowAnnotation::NextElementShadowVariable {
72            source_variable_name: source_variable_name.into(),
73        }
74    }
75
76    pub fn anchor(source_variable_name: impl Into<String>) -> Self {
77        ShadowAnnotation::AnchorShadowVariable {
78            source_variable_name: source_variable_name.into(),
79        }
80    }
81
82    pub fn piggyback(shadow_variable_name: impl Into<String>) -> Self {
83        ShadowAnnotation::PiggybackShadowVariable {
84            shadow_variable_name: shadow_variable_name.into(),
85        }
86    }
87
88    pub fn cascading_update(target_method_name: impl Into<String>) -> Self {
89        ShadowAnnotation::CascadingUpdateShadowVariable {
90            target_method_name: target_method_name.into(),
91        }
92    }
93
94    pub fn source_variable_name(&self) -> Option<&str> {
95        match self {
96            ShadowAnnotation::ShadowVariable {
97                source_variable_name,
98                ..
99            } => Some(source_variable_name),
100            ShadowAnnotation::InverseRelationShadowVariable {
101                source_variable_name,
102            } => Some(source_variable_name),
103            ShadowAnnotation::IndexShadowVariable {
104                source_variable_name,
105            } => Some(source_variable_name),
106            ShadowAnnotation::PreviousElementShadowVariable {
107                source_variable_name,
108            } => Some(source_variable_name),
109            ShadowAnnotation::NextElementShadowVariable {
110                source_variable_name,
111            } => Some(source_variable_name),
112            ShadowAnnotation::AnchorShadowVariable {
113                source_variable_name,
114            } => Some(source_variable_name),
115            ShadowAnnotation::PiggybackShadowVariable { .. } => None,
116            ShadowAnnotation::CascadingUpdateShadowVariable { .. } => None,
117        }
118    }
119
120    pub fn is_list_variable_shadow(&self) -> bool {
121        matches!(
122            self,
123            ShadowAnnotation::IndexShadowVariable { .. }
124                | ShadowAnnotation::PreviousElementShadowVariable { .. }
125                | ShadowAnnotation::NextElementShadowVariable { .. }
126                | ShadowAnnotation::AnchorShadowVariable { .. }
127        )
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_shadow_variable() {
137        let ann = ShadowAnnotation::shadow_variable("room");
138        match ann {
139            ShadowAnnotation::ShadowVariable {
140                source_variable_name,
141                source_entity_class,
142            } => {
143                assert_eq!(source_variable_name, "room");
144                assert!(source_entity_class.is_none());
145            }
146            _ => panic!("Expected ShadowVariable"),
147        }
148    }
149
150    #[test]
151    fn test_shadow_variable_with_class() {
152        let ann = ShadowAnnotation::shadow_variable_with_class("room", "Lesson");
153        match ann {
154            ShadowAnnotation::ShadowVariable {
155                source_variable_name,
156                source_entity_class,
157            } => {
158                assert_eq!(source_variable_name, "room");
159                assert_eq!(source_entity_class, Some("Lesson".to_string()));
160            }
161            _ => panic!("Expected ShadowVariable"),
162        }
163    }
164
165    #[test]
166    fn test_inverse_relation() {
167        let ann = ShadowAnnotation::inverse_relation("visits");
168        match ann {
169            ShadowAnnotation::InverseRelationShadowVariable {
170                source_variable_name,
171            } => {
172                assert_eq!(source_variable_name, "visits");
173            }
174            _ => panic!("Expected InverseRelationShadowVariable"),
175        }
176    }
177
178    #[test]
179    fn test_index() {
180        let ann = ShadowAnnotation::index("taskList");
181        match ann {
182            ShadowAnnotation::IndexShadowVariable {
183                source_variable_name,
184            } => {
185                assert_eq!(source_variable_name, "taskList");
186            }
187            _ => panic!("Expected IndexShadowVariable"),
188        }
189    }
190
191    #[test]
192    fn test_previous_element() {
193        let ann = ShadowAnnotation::previous_element("taskList");
194        match ann {
195            ShadowAnnotation::PreviousElementShadowVariable {
196                source_variable_name,
197            } => {
198                assert_eq!(source_variable_name, "taskList");
199            }
200            _ => panic!("Expected PreviousElementShadowVariable"),
201        }
202    }
203
204    #[test]
205    fn test_next_element() {
206        let ann = ShadowAnnotation::next_element("taskList");
207        match ann {
208            ShadowAnnotation::NextElementShadowVariable {
209                source_variable_name,
210            } => {
211                assert_eq!(source_variable_name, "taskList");
212            }
213            _ => panic!("Expected NextElementShadowVariable"),
214        }
215    }
216
217    #[test]
218    fn test_anchor() {
219        let ann = ShadowAnnotation::anchor("taskList");
220        match ann {
221            ShadowAnnotation::AnchorShadowVariable {
222                source_variable_name,
223            } => {
224                assert_eq!(source_variable_name, "taskList");
225            }
226            _ => panic!("Expected AnchorShadowVariable"),
227        }
228    }
229
230    #[test]
231    fn test_piggyback() {
232        let ann = ShadowAnnotation::piggyback("arrivalTime");
233        match ann {
234            ShadowAnnotation::PiggybackShadowVariable {
235                shadow_variable_name,
236            } => {
237                assert_eq!(shadow_variable_name, "arrivalTime");
238            }
239            _ => panic!("Expected PiggybackShadowVariable"),
240        }
241    }
242
243    #[test]
244    fn test_cascading_update() {
245        let ann = ShadowAnnotation::cascading_update("updateArrivalTime");
246        match ann {
247            ShadowAnnotation::CascadingUpdateShadowVariable { target_method_name } => {
248                assert_eq!(target_method_name, "updateArrivalTime");
249            }
250            _ => panic!("Expected CascadingUpdateShadowVariable"),
251        }
252    }
253
254    #[test]
255    fn test_source_variable_name() {
256        assert_eq!(
257            ShadowAnnotation::shadow_variable("room").source_variable_name(),
258            Some("room")
259        );
260        assert_eq!(
261            ShadowAnnotation::inverse_relation("visits").source_variable_name(),
262            Some("visits")
263        );
264        assert_eq!(
265            ShadowAnnotation::index("tasks").source_variable_name(),
266            Some("tasks")
267        );
268        assert_eq!(
269            ShadowAnnotation::previous_element("tasks").source_variable_name(),
270            Some("tasks")
271        );
272        assert_eq!(
273            ShadowAnnotation::next_element("tasks").source_variable_name(),
274            Some("tasks")
275        );
276        assert_eq!(
277            ShadowAnnotation::anchor("tasks").source_variable_name(),
278            Some("tasks")
279        );
280        assert_eq!(
281            ShadowAnnotation::piggyback("time").source_variable_name(),
282            None
283        );
284        assert_eq!(
285            ShadowAnnotation::cascading_update("update").source_variable_name(),
286            None
287        );
288    }
289
290    #[test]
291    fn test_is_list_variable_shadow() {
292        assert!(!ShadowAnnotation::shadow_variable("room").is_list_variable_shadow());
293        assert!(!ShadowAnnotation::inverse_relation("visits").is_list_variable_shadow());
294        assert!(ShadowAnnotation::index("tasks").is_list_variable_shadow());
295        assert!(ShadowAnnotation::previous_element("tasks").is_list_variable_shadow());
296        assert!(ShadowAnnotation::next_element("tasks").is_list_variable_shadow());
297        assert!(ShadowAnnotation::anchor("tasks").is_list_variable_shadow());
298        assert!(!ShadowAnnotation::piggyback("time").is_list_variable_shadow());
299        assert!(!ShadowAnnotation::cascading_update("update").is_list_variable_shadow());
300    }
301
302    #[test]
303    fn test_json_serialization() {
304        let annotations = vec![
305            ShadowAnnotation::shadow_variable("room"),
306            ShadowAnnotation::shadow_variable_with_class("room", "Lesson"),
307            ShadowAnnotation::inverse_relation("visits"),
308            ShadowAnnotation::index("tasks"),
309            ShadowAnnotation::previous_element("tasks"),
310            ShadowAnnotation::next_element("tasks"),
311            ShadowAnnotation::anchor("tasks"),
312            ShadowAnnotation::piggyback("arrivalTime"),
313            ShadowAnnotation::cascading_update("updateTime"),
314        ];
315
316        for ann in annotations {
317            let json = serde_json::to_string(&ann).unwrap();
318            let parsed: ShadowAnnotation = serde_json::from_str(&json).unwrap();
319            assert_eq!(parsed, ann);
320        }
321    }
322
323    #[test]
324    fn test_json_format() {
325        let ann = ShadowAnnotation::shadow_variable_with_class("room", "Lesson");
326        let json = serde_json::to_string(&ann).unwrap();
327        assert!(json.contains("\"type\":\"ShadowVariable\""));
328        assert!(json.contains("\"source_variable_name\":\"room\""));
329        assert!(json.contains("\"source_entity_class\":\"Lesson\""));
330    }
331}