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}