nimbus_fml/backends/swift/gen_structs/
object.rs
1use askama::Template;
6use std::fmt::Display;
7
8use crate::backends::{
9 CodeDeclaration, CodeOracle, CodeType, LiteralRenderer, TypeIdentifier, VariablesType,
10};
11use crate::intermediate_representation::{FeatureManifest, Literal, ObjectDef};
12
13use super::filters;
14
15use super::common::{self, code_type};
16
17pub struct ObjectRuntime;
18
19impl CodeDeclaration for ObjectRuntime {}
20
21pub struct ObjectCodeType {
22 id: String,
23}
24
25impl ObjectCodeType {
26 pub fn new(id: String) -> Self {
27 Self { id }
28 }
29}
30
31impl CodeType for ObjectCodeType {
32 fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
33 common::class_name(&self.id)
34 }
35
36 fn property_getter(
37 &self,
38 oracle: &dyn CodeOracle,
39 vars: &dyn Display,
40 prop: &dyn Display,
41 default: &dyn Display,
42 ) -> String {
43 code_type::property_getter(self, oracle, vars, prop, default)
44 }
45
46 fn value_getter(
47 &self,
48 oracle: &dyn CodeOracle,
49 vars: &dyn Display,
50 prop: &dyn Display,
51 ) -> String {
52 code_type::value_getter(self, oracle, vars, prop)
53 }
54
55 fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
56 let transform = self.create_transform(oracle)?;
57 Some(format!("map({})", transform))
58 }
59
60 fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
65 VariablesType::Variables
66 }
67
68 fn create_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
69 Some(format!("{}.create", self.type_label(oracle)))
70 }
71
72 fn merge_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
73 Some(format!("{}.mergeWith", self.type_label(oracle)))
74 }
75
76 fn value_merger(&self, _oracle: &dyn CodeOracle, default: &dyn Display) -> Option<String> {
77 Some(format!("_mergeWith({})", default))
78 }
79
80 fn literal(
81 &self,
82 oracle: &dyn CodeOracle,
83 ctx: &dyn Display,
84 renderer: &dyn LiteralRenderer,
85 literal: &Literal,
86 ) -> String {
87 renderer.literal(
88 oracle,
89 &TypeIdentifier::Object(self.id.clone()),
90 literal,
91 ctx,
92 )
93 }
94}
95
96#[derive(Template)]
97#[template(syntax = "swift", escape = "none", path = "ObjectTemplate.swift")]
98pub(crate) struct ObjectCodeDeclaration {
99 inner: ObjectDef,
100 fm: FeatureManifest,
101}
102
103impl ObjectCodeDeclaration {
104 pub fn new(fm: &FeatureManifest, inner: &ObjectDef) -> Self {
105 Self {
106 fm: fm.clone(),
107 inner: inner.clone(),
108 }
109 }
110 pub fn inner(&self) -> ObjectDef {
111 self.inner.clone()
112 }
113}
114
115impl CodeDeclaration for ObjectCodeDeclaration {
116 fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
117 Some(self.render().unwrap())
118 }
119}
120
121impl LiteralRenderer for ObjectCodeDeclaration {
122 fn literal(
123 &self,
124 oracle: &dyn CodeOracle,
125 typ: &TypeIdentifier,
126 value: &Literal,
127 ctx: &dyn Display,
128 ) -> String {
129 object_literal(&self.fm, &self, oracle, typ, value, ctx)
130 }
131}
132
133pub(crate) fn object_literal(
134 fm: &FeatureManifest,
135 renderer: &dyn LiteralRenderer,
136 oracle: &dyn CodeOracle,
137 typ: &TypeIdentifier,
138 value: &Literal,
139 ctx: &dyn Display,
140) -> String {
141 let id = if let TypeIdentifier::Object(id) = typ {
142 id
143 } else {
144 return oracle.find(typ).literal(oracle, ctx, renderer, value);
145 };
146 let literal_map = if let Literal::Object(map) = value {
147 map
148 } else {
149 unreachable!(
150 "An JSON object is expected for {} object literal",
151 oracle.find(typ).type_label(oracle)
152 )
153 };
154
155 let def = fm.find_object(id).unwrap();
156 let args: Vec<String> = def
157 .props()
158 .iter()
159 .filter_map(|prop_def| {
160 literal_map.get(&prop_def.name()).map(|v| {
161 format!(
162 "{var_name}: {var_value}",
163 var_name = common::var_name(&prop_def.name()),
164 var_value = oracle.find(&prop_def.typ).literal(oracle, ctx, renderer, v)
165 )
166 })
167 })
168 .collect();
169
170 format!(
171 "{typelabel}({args})",
172 typelabel = oracle.find(typ).type_label(oracle),
173 args = args.join(", ")
174 )
175}
176
177#[cfg(test)]
178mod unit_tests {
179 use serde_json::json;
180
181 use crate::{backends::TypeIdentifier, intermediate_representation::Literal};
182
183 use super::*;
184
185 struct TestCodeOracle;
186 impl CodeOracle for TestCodeOracle {
187 fn find(&self, _type_: &TypeIdentifier) -> Box<dyn CodeType> {
188 unreachable!()
189 }
190 }
191
192 struct TestRenderer;
193 impl LiteralRenderer for TestRenderer {
194 fn literal(
195 &self,
196 _oracle: &dyn CodeOracle,
197 typ: &TypeIdentifier,
198 _value: &Literal,
199 _ctx: &dyn Display,
200 ) -> String {
201 if let TypeIdentifier::Object(nm) = typ {
202 format!("{}()", nm)
203 } else {
204 unreachable!()
205 }
206 }
207 }
208
209 fn oracle() -> Box<dyn CodeOracle> {
210 Box::new(TestCodeOracle) as Box<dyn CodeOracle>
211 }
212
213 fn code_type(name: &str) -> Box<dyn CodeType> {
214 Box::new(ObjectCodeType::new(name.to_string())) as Box<dyn CodeType>
215 }
216
217 fn getter_with_fallback(
218 ct: &dyn CodeType,
219 vars: &dyn Display,
220 prop: &dyn Display,
221 def: &dyn Display,
222 ) -> String {
223 let oracle = &*oracle();
224 ct.property_getter(oracle, vars, prop, def)
225 }
226
227 #[test]
228 fn test_type_label() {
229 let ct = code_type("AnObject");
230 let oracle = &*oracle();
231 assert_eq!("AnObject".to_string(), ct.type_label(oracle))
232 }
233
234 #[test]
235 fn test_literal() {
236 let ct = code_type("AnObject");
237 let oracle = &*oracle();
238 let finder = &TestRenderer;
239 let ctx = String::from("ctx");
240 assert_eq!(
241 "AnObject()".to_string(),
242 ct.literal(oracle, &ctx, finder, &json!({}))
243 );
244 }
245
246 #[test]
247 fn test_get_value() {
248 let ct = code_type("AnObject");
249 let oracle = &*oracle();
250
251 assert_eq!(
252 r#"v.getVariables("the-property")"#.to_string(),
253 ct.value_getter(oracle, &"v", &"the-property")
254 );
255 }
256
257 #[test]
258 fn test_getter_with_fallback() {
259 let ct = code_type("AnObject");
260 assert_eq!(
261 r#"vars.getVariables("the-property")?.map(AnObject.create)._mergeWith(default) ?? default"#.to_string(),
262 getter_with_fallback(&*ct, &"vars", &"the-property", &"default")
263 );
264 }
265}