1use std::fmt::Display;
6
7use super::common::{self, code_type};
8use crate::backends::{LiteralRenderer, VariablesType};
9use crate::{
10 backends::{CodeOracle, CodeType, TypeIdentifier},
11 intermediate_representation::Literal,
12};
13
14pub(crate) struct OptionalCodeType {
15 inner: TypeIdentifier,
16}
17
18impl OptionalCodeType {
19 pub(crate) fn new(inner: &TypeIdentifier) -> Self {
20 Self {
21 inner: inner.clone(),
22 }
23 }
24}
25
26impl CodeType for OptionalCodeType {
27 fn type_label(&self, oracle: &dyn CodeOracle) -> String {
30 format!(
31 "{item}?",
32 item = oracle.find(&self.inner).type_label(oracle),
33 )
34 }
35
36 fn property_getter(
38 &self,
39 oracle: &dyn CodeOracle,
40 vars: &dyn Display,
41 prop: &dyn Display,
42 default: &dyn Display,
43 ) -> String {
44 code_type::property_getter(self, oracle, vars, prop, default)
46 }
47
48 fn value_getter(
49 &self,
50 oracle: &dyn CodeOracle,
51 vars: &dyn Display,
52 prop: &dyn Display,
53 ) -> String {
54 code_type::value_getter(self, oracle, vars, prop)
55 }
56
57 fn create_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
60 oracle.find(&self.inner).create_transform(oracle)
61 }
62
63 fn variables_type(&self, oracle: &dyn CodeOracle) -> VariablesType {
66 oracle.find(&self.inner).variables_type(oracle)
67 }
68
69 fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
72 oracle.find(&self.inner).value_mapper(oracle)
73 }
74
75 fn value_merger(&self, oracle: &dyn CodeOracle, default: &dyn Display) -> Option<String> {
81 oracle.find(&self.inner).value_merger(oracle, default)
82 }
83
84 fn defaults_type(&self, oracle: &dyn CodeOracle) -> String {
85 format!("{}?", oracle.find(&self.inner).defaults_type(oracle))
86 }
87
88 fn defaults_mapper(
89 &self,
90 oracle: &dyn CodeOracle,
91 value: &dyn Display,
92 vars: &dyn Display,
93 ) -> Option<String> {
94 let id = "$0";
95 let mapper = oracle
96 .find(&self.inner)
97 .defaults_mapper(oracle, &id, vars)?;
98 Some(format!(
99 "{value}.map {{ {mapper} }}",
100 value = value,
101 mapper = mapper
102 ))
103 }
104
105 fn as_json_transform(&self, oracle: &dyn CodeOracle, prop: &dyn Display) -> Option<String> {
107 let prop = format!("{}?", prop);
110 oracle.find(&self.inner).as_json_transform(oracle, &prop)
111 }
112
113 fn literal(
116 &self,
117 oracle: &dyn CodeOracle,
118 ctx: &dyn Display,
119 renderer: &dyn LiteralRenderer,
120 literal: &Literal,
121 ) -> String {
122 match literal {
123 serde_json::Value::Null => "nil".to_string(),
124 _ => oracle
125 .find(&self.inner)
126 .literal(oracle, ctx, renderer, literal),
127 }
128 }
129}
130
131pub(crate) struct MapCodeType {
134 k_type: TypeIdentifier,
135 v_type: TypeIdentifier,
136}
137
138impl MapCodeType {
139 pub(crate) fn new(k: &TypeIdentifier, v: &TypeIdentifier) -> Self {
140 Self {
141 k_type: k.clone(),
142 v_type: v.clone(),
143 }
144 }
145}
146
147impl CodeType for MapCodeType {
148 fn property_getter(
149 &self,
150 oracle: &dyn CodeOracle,
151 vars: &dyn Display,
152 prop: &dyn Display,
153 default: &dyn Display,
154 ) -> String {
155 code_type::property_getter(self, oracle, vars, prop, default)
156 }
157
158 fn type_label(&self, oracle: &dyn CodeOracle) -> String {
161 format!(
162 "[{k}: {v}]",
163 k = oracle.find(&self.k_type).type_label(oracle),
164 v = oracle.find(&self.v_type).type_label(oracle),
165 )
166 }
167
168 fn value_getter(
169 &self,
170 oracle: &dyn CodeOracle,
171 vars: &dyn Display,
172 prop: &dyn Display,
173 ) -> String {
174 let v_type = oracle.find(&self.v_type);
175 format!(
176 "{vars}.get{vt}Map({prop})",
177 vars = vars,
178 vt = v_type.variables_type(oracle),
179 prop = common::quoted(prop),
180 )
181 }
182
183 fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
184 let k_type = oracle.find(&self.k_type);
185 let v_type = oracle.find(&self.v_type);
186 Some(
187 match (
188 k_type.create_transform(oracle),
189 v_type.create_transform(oracle),
190 ) {
191 (Some(k), Some(v)) => format!("mapEntriesNotNull({k}, {v})", k = k, v = v),
192 (None, Some(v)) => format!("mapValuesNotNull({v})", v = v),
193 (Some(k), None) => format!("mapKeysNotNull({k})", k = k),
195 _ => return None,
196 },
197 )
198 }
199
200 fn value_merger(&self, oracle: &dyn CodeOracle, default: &dyn Display) -> Option<String> {
201 let v_type = oracle.find(&self.v_type);
202 Some(match v_type.merge_transform(oracle) {
203 Some(transform) => format!(
204 "mergeWith({default}, {transform})",
205 default = default,
206 transform = transform
207 ),
208 None => format!("mergeWith({})", default),
209 })
210 }
211
212 fn create_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
213 let vtype = oracle.find(&self.v_type).variables_type(oracle);
214
215 self.value_mapper(oracle)
216 .map(|mapper| {
217 format!(
218 r#"{{ (_vars) in return _vars.as{vtype}Map()?.{mapper} }}"#,
219 vtype = vtype,
220 mapper = mapper
221 )
222 })
223 .or_else(|| {
224 Some(format!(
225 r#"{{ (_vars) in return _vars.as{vtype}Map()? }}"#,
226 vtype = vtype
227 ))
228 })
229 }
230
231 fn merge_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
232 let overrides = "_overrides";
233 let defaults = "_defaults";
234
235 self.value_merger(oracle, &defaults).map(|merger| {
236 format!(
237 r#"{{ ({overrides}, {defaults}) in return {overrides}.{merger} }}"#,
238 overrides = overrides,
239 defaults = defaults,
240 merger = merger
241 )
242 })
243 }
244
245 fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
248 VariablesType::Variables
249 }
250
251 fn defaults_type(&self, oracle: &dyn CodeOracle) -> String {
252 let k = oracle.find(&self.k_type).defaults_type(oracle);
253 let v = oracle.find(&self.v_type).defaults_type(oracle);
254 format!("[{k}: {v}]", k = k, v = v)
255 }
256
257 fn defaults_mapper(
258 &self,
259 oracle: &dyn CodeOracle,
260 value: &dyn Display,
261 vars: &dyn Display,
262 ) -> Option<String> {
263 let id = "$0";
264 let mapper = oracle
265 .find(&self.v_type)
266 .defaults_mapper(oracle, &id, vars)?;
267 Some(format!(
268 "{value}.mapValues {{ {mapper} }}",
269 value = value,
270 mapper = mapper
271 ))
272 }
273
274 fn as_json_transform(&self, oracle: &dyn CodeOracle, prop: &dyn Display) -> Option<String> {
275 let k_type = oracle.find(&self.k_type);
276 let v_type = oracle.find(&self.v_type);
277 Some(
278 match (
279 k_type.as_json_transform(oracle, &"$0"),
280 v_type.as_json_transform(oracle, &"$0"),
281 ) {
282 (Some(k), Some(v)) => {
283 format!(
284 "{prop}.mapEntriesNotNull({{ {k} }}, {{ {v} }})",
285 prop = prop,
286 k = k,
287 v = v
288 )
289 }
290 (None, Some(v)) => {
291 format!("{prop}.mapValuesNotNull {{ {v} }}", prop = prop, v = v)
292 }
293 (Some(k), None) => {
295 format!("{prop}.mapKeysNotNull {{ {k} }}", prop = prop, k = k)
296 }
297 _ => return None,
298 },
299 )
300 }
301
302 fn literal(
305 &self,
306 oracle: &dyn CodeOracle,
307 ctx: &dyn Display,
308 renderer: &dyn LiteralRenderer,
309 literal: &Literal,
310 ) -> String {
311 let variant = match literal {
312 serde_json::Value::Object(v) => v,
313 _ => unreachable!(),
314 };
315 let k_type = oracle.find(&self.k_type);
316 let v_type = oracle.find(&self.v_type);
317 let src: Vec<String> = variant
318 .iter()
319 .map(|(k, v)| {
320 format!(
321 "{k}: {v}",
322 k = k_type.literal(oracle, ctx, renderer, &Literal::String(k.clone())),
323 v = v_type.literal(oracle, ctx, renderer, v)
324 )
325 })
326 .collect();
327
328 if src.is_empty() {
329 "[:]".to_string()
330 } else {
331 format!("[{}]", src.join(", "))
332 }
333 }
334}
335
336pub(crate) struct ListCodeType {
339 inner: TypeIdentifier,
340}
341
342impl ListCodeType {
343 pub(crate) fn new(inner: &TypeIdentifier) -> Self {
344 Self {
345 inner: inner.clone(),
346 }
347 }
348}
349
350impl CodeType for ListCodeType {
351 fn type_label(&self, oracle: &dyn CodeOracle) -> String {
354 format!(
355 "[{item}]",
356 item = oracle.find(&self.inner).type_label(oracle),
357 )
358 }
359
360 fn property_getter(
361 &self,
362 oracle: &dyn CodeOracle,
363 vars: &dyn Display,
364 prop: &dyn Display,
365 default: &dyn Display,
366 ) -> String {
367 code_type::property_getter(self, oracle, vars, prop, default)
368 }
369
370 fn value_getter(
371 &self,
372 oracle: &dyn CodeOracle,
373 vars: &dyn Display,
374 prop: &dyn Display,
375 ) -> String {
376 let vtype = oracle.find(&self.inner).variables_type(oracle);
377 format!(
378 "{vars}.get{vt}List(\"{prop}\")",
379 vars = vars,
380 vt = vtype,
381 prop = prop
382 )
383 }
384
385 fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
386 let transform = oracle.find(&self.inner).create_transform(oracle)?;
387 Some(if transform.starts_with('{') {
388 format!("mapNotNull {}", transform)
389 } else {
390 format!("mapNotNull({})", transform)
391 })
392 }
393
394 fn value_merger(&self, _oracle: &dyn CodeOracle, _default: &dyn Display) -> Option<String> {
395 None
397 }
398
399 fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
402 unimplemented!("Lists and maps of lists aren't supported. The workaround is to use a list of map of list holder objects")
405 }
406
407 fn defaults_type(&self, oracle: &dyn CodeOracle) -> String {
408 format!("[{}]", oracle.find(&self.inner).defaults_type(oracle))
409 }
410
411 fn defaults_mapper(
412 &self,
413 oracle: &dyn CodeOracle,
414 value: &dyn Display,
415 vars: &dyn Display,
416 ) -> Option<String> {
417 let id = "$0";
418 let mapper = oracle
419 .find(&self.inner)
420 .defaults_mapper(oracle, &id, vars)?;
421 Some(format!(
422 "{value}.map {{ {mapper} }}",
423 value = value,
424 mapper = mapper
425 ))
426 }
427
428 fn as_json_transform(&self, oracle: &dyn CodeOracle, prop: &dyn Display) -> Option<String> {
429 let mapper = oracle.find(&self.inner).as_json_transform(oracle, &"$0")?;
430 Some(format!(
431 "{prop}.map {{ {mapper} }}",
432 prop = prop,
433 mapper = mapper
434 ))
435 }
436
437 fn literal(
440 &self,
441 oracle: &dyn CodeOracle,
442 ctx: &dyn Display,
443 renderer: &dyn LiteralRenderer,
444 literal: &Literal,
445 ) -> String {
446 let variant = match literal {
447 serde_json::Value::Array(v) => v,
448 _ => unreachable!(),
449 };
450
451 let v_type = oracle.find(&self.inner);
452 let src: Vec<String> = variant
453 .iter()
454 .map(|v| v_type.literal(oracle, ctx, renderer, v))
455 .collect();
456
457 format!("[{}]", src.join(", "))
458 }
459}
460
461#[cfg(test)]
462mod unit_tests {
463
464 use serde_json::json;
465
466 use crate::backends::swift::gen_structs::{
467 enum_::EnumCodeType, object::ObjectCodeType, primitives::StringCodeType,
468 };
469 use crate::backends::TypeIdentifier;
470
471 use super::*;
472
473 struct TestCodeOracle;
474 impl CodeOracle for TestCodeOracle {
475 fn find(&self, type_: &TypeIdentifier) -> Box<dyn CodeType> {
476 match type_ {
477 TypeIdentifier::String => Box::new(StringCodeType) as Box<dyn CodeType>,
478 TypeIdentifier::Enum(s) => {
479 Box::new(EnumCodeType::new(s.clone())) as Box<dyn CodeType>
480 }
481 TypeIdentifier::Object(s) => {
482 Box::new(ObjectCodeType::new(s.clone())) as Box<dyn CodeType>
483 }
484 TypeIdentifier::List(i) => Box::new(ListCodeType::new(i)),
485 TypeIdentifier::EnumMap(k, v) => Box::new(MapCodeType::new(k, v)),
486 _ => unreachable!(),
487 }
488 }
489 }
490
491 struct TestRenderer;
492 impl LiteralRenderer for TestRenderer {
493 fn literal(
494 &self,
495 _oracle: &dyn CodeOracle,
496 _typ: &TypeIdentifier,
497 _value: &Literal,
498 _ctx: &dyn Display,
499 ) -> String {
500 unreachable!()
501 }
502 }
503
504 fn oracle() -> Box<dyn CodeOracle> {
505 Box::new(TestCodeOracle) as Box<dyn CodeOracle>
506 }
507
508 fn type_(nm: &str) -> TypeIdentifier {
509 match nm {
510 "String" => TypeIdentifier::String,
511 "AnObject" => TypeIdentifier::Object("AnObject".to_string()),
512 nm => TypeIdentifier::Enum(nm.to_string()),
513 }
514 }
515
516 fn list_type(item: &str) -> Box<dyn CodeType> {
517 Box::new(ListCodeType::new(&type_(item)))
518 }
519
520 fn map_type(k: &str, v: &str) -> Box<dyn CodeType> {
521 Box::new(MapCodeType::new(&type_(k), &type_(v)))
522 }
523
524 fn getter_with_fallback(
525 ct: &dyn CodeType,
526 vars: &dyn Display,
527 prop: &dyn Display,
528 def: &dyn Display,
529 ) -> String {
530 let oracle = &*oracle();
531 ct.property_getter(oracle, vars, prop, def)
532 }
533
534 #[test]
535 fn test_list_type_label() {
536 let oracle = &*oracle();
537 let ct = list_type("String");
538 assert_eq!("[String]".to_string(), ct.type_label(oracle));
539
540 let ct = list_type("AnEnum");
541 assert_eq!("[AnEnum]".to_string(), ct.type_label(oracle));
542 }
543
544 #[test]
545 fn test_list_literal() {
546 let oracle = &*oracle();
547 let finder = &TestRenderer;
548 let ctx = String::from("ctx");
549 let ct = list_type("String");
550 assert_eq!(
551 r#"["x", "y", "z"]"#.to_string(),
552 ct.literal(oracle, &ctx, finder, &json!(["x", "y", "z"]))
553 );
554
555 let ct = list_type("AnEnum");
556 assert_eq!(
557 r#"[.x, .y, .z]"#.to_string(),
558 ct.literal(oracle, &ctx, finder, &json!(["x", "y", "z"]))
559 );
560 }
561
562 #[test]
563 fn test_list_get_value() {
564 let oracle = &*oracle();
565
566 let ct = list_type("AnEnum");
567 assert_eq!(
568 r#"v.getStringList("the-property")"#.to_string(),
569 ct.value_getter(oracle, &"v", &"the-property")
570 );
571
572 let ct = list_type("AnObject");
573 assert_eq!(
574 r#"v.getVariablesList("the-property")"#.to_string(),
575 ct.value_getter(oracle, &"v", &"the-property")
576 );
577
578 let ct = list_type("String");
579 assert_eq!(
580 r#"v.getStringList("the-property")"#.to_string(),
581 ct.value_getter(oracle, &"v", &"the-property")
582 );
583 }
584
585 #[test]
586 fn test_list_getter_with_fallback() {
587 let ct = list_type("String");
588 assert_eq!(
589 r#"vars.getStringList("the-property") ?? default"#.to_string(),
590 getter_with_fallback(&*ct, &"vars", &"the-property", &"default")
591 );
592
593 let ct = list_type("AnEnum");
594 assert_eq!(
595 r#"vars.getStringList("the-property")?.mapNotNull(AnEnum.enumValue) ?? default"#
596 .to_string(),
597 getter_with_fallback(&*ct, &"vars", &"the-property", &"default")
598 );
599
600 let ct = list_type("AnObject");
601 assert_eq!(
602 r#"vars.getVariablesList("the-property")?.mapNotNull(AnObject.create) ?? default"#
603 .to_string(),
604 getter_with_fallback(&*ct, &"vars", &"the-property", &"default")
605 );
606 }
607
608 #[test]
609 fn test_map_type_label() {
610 let oracle = &*oracle();
611 let ct = map_type("String", "String");
612 assert_eq!("[String: String]".to_string(), ct.type_label(oracle));
613
614 let ct = map_type("String", "AnEnum");
615 assert_eq!("[String: AnEnum]".to_string(), ct.type_label(oracle));
616 }
617
618 #[test]
619 fn test_map_literal() {
620 let oracle = &*oracle();
621 let finder = &TestRenderer;
622 let ctx = String::from("ctx");
623
624 let ct = map_type("String", "AnEnum");
625 assert_eq!(
626 r#"["a": .a, "b": .b]"#.to_string(),
627 ct.literal(oracle, &ctx, finder, &json!({"a": "a", "b": "b"}))
628 );
629
630 let ct = map_type("AnEnum", "String");
631 assert_eq!(
632 r#"[.a: "a", .b: "b"]"#.to_string(),
633 ct.literal(oracle, &ctx, finder, &json!({"a": "a", "b": "b"}))
634 );
635 }
636
637 #[test]
638 fn test_map_get_value() {
639 let oracle = &*oracle();
640
641 let ct = map_type("String", "AnEnum");
642 assert_eq!(
643 r#"v.getStringMap("the-property")"#.to_string(),
644 ct.value_getter(oracle, &"v", &"the-property")
645 );
646
647 let ct = map_type("AnEnum", "String");
648 assert_eq!(
649 r#"v.getStringMap("the-property")"#.to_string(),
650 ct.value_getter(oracle, &"v", &"the-property")
651 );
652
653 let ct = map_type("AnEnum", "Another");
654 assert_eq!(
655 r#"v.getStringMap("the-property")"#.to_string(),
656 ct.value_getter(oracle, &"v", &"the-property")
657 );
658 }
659
660 #[test]
661 fn test_map_getter_with_fallback() {
662 let oracle = &*oracle();
663
664 let ct = map_type("String", "AnEnum");
665 assert_eq!(
666 r#"v.getStringMap("the-property")?.mapValuesNotNull(AnEnum.enumValue).mergeWith(def) ?? def"#.to_string(),
667 ct.property_getter(oracle, &"v", &"the-property", &"def")
668 );
669
670 let ct = map_type("AnEnum", "String");
671 assert_eq!(
672 r#"v.getStringMap("the-property")?.mapKeysNotNull(AnEnum.enumValue).mergeWith(def) ?? def"#
673 .to_string(),
674 ct.property_getter(oracle, &"v", &"the-property", &"def")
675 );
676
677 let ct = map_type("AnEnum", "Another");
678 assert_eq!(
679 r#"v.getStringMap("the-property")?.mapEntriesNotNull(AnEnum.enumValue, Another.enumValue).mergeWith(def) ?? def"#
680 .to_string(),
681 ct.property_getter(oracle, &"v", &"the-property", &"def")
682 );
683
684 let ct = map_type("AnEnum", "AnObject");
685 assert_eq!(
686 r#"v.getVariablesMap("the-property")?.mapEntriesNotNull(AnEnum.enumValue, AnObject.create).mergeWith(def, AnObject.mergeWith) ?? def"#.to_string(),
687 ct.property_getter(oracle, &"v", &"the-property", &"def"));
688 }
689}