nimbus_fml/backends/kotlin/gen_structs/
primitives.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::fmt::Display;
6
7use super::common::{code_type, quoted};
8use crate::backends::{CodeOracle, CodeType, LiteralRenderer, VariablesType};
9use crate::intermediate_representation::Literal;
10
11pub(crate) struct BooleanCodeType;
12
13impl CodeType for BooleanCodeType {
14    /// The language specific label used to reference this type. This will be used in
15    /// method signatures and property declarations.
16    fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
17        "Boolean".into()
18    }
19
20    fn property_getter(
21        &self,
22        oracle: &dyn CodeOracle,
23        vars: &dyn Display,
24        prop: &dyn Display,
25        default: &dyn Display,
26    ) -> String {
27        code_type::property_getter(self, oracle, vars, prop, default)
28    }
29
30    fn value_getter(
31        &self,
32        oracle: &dyn CodeOracle,
33        vars: &dyn Display,
34        prop: &dyn Display,
35    ) -> String {
36        code_type::value_getter(self, oracle, vars, prop)
37    }
38
39    fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
40        code_type::value_mapper(self, oracle)
41    }
42
43    /// The name of the type as it's represented in the `Variables` object.
44    /// The string return may be used to combine with an identifier, e.g. a `Variables` method name.
45    fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
46        VariablesType::Bool
47    }
48
49    /// A representation of the given literal for this type.
50    /// N.B. `Literal` is aliased from `serde_json::Value`.
51    fn literal(
52        &self,
53        _oracle: &dyn CodeOracle,
54        _ctx: &dyn Display,
55        _renderer: &dyn LiteralRenderer,
56        literal: &Literal,
57    ) -> String {
58        match literal {
59            serde_json::Value::Bool(v) => {
60                if *v {
61                    "true".to_string()
62                } else {
63                    "false".to_string()
64                }
65            }
66            _ => unreachable!("Expecting a boolean"),
67        }
68    }
69
70    fn preference_getter(
71        &self,
72        _oracle: &dyn CodeOracle,
73        prefs: &dyn Display,
74        pref_key: &dyn Display,
75    ) -> Option<String> {
76        Some(format!("{prefs}.getBoolean({}, false)", quoted(pref_key)))
77    }
78}
79
80pub(crate) struct IntCodeType;
81
82impl CodeType for IntCodeType {
83    /// The language specific label used to reference this type. This will be used in
84    /// method signatures and property declarations.
85    fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
86        "Int".into()
87    }
88
89    fn property_getter(
90        &self,
91        oracle: &dyn CodeOracle,
92        vars: &dyn Display,
93        prop: &dyn Display,
94        default: &dyn Display,
95    ) -> String {
96        code_type::property_getter(self, oracle, vars, prop, default)
97    }
98
99    fn value_getter(
100        &self,
101        oracle: &dyn CodeOracle,
102        vars: &dyn Display,
103        prop: &dyn Display,
104    ) -> String {
105        code_type::value_getter(self, oracle, vars, prop)
106    }
107
108    fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
109        code_type::value_mapper(self, oracle)
110    }
111
112    /// The name of the type as it's represented in the `Variables` object.
113    /// The string return may be used to combine with an identifier, e.g. a `Variables` method name.
114    fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
115        VariablesType::Int
116    }
117
118    /// A representation of the given literal for this type.
119    /// N.B. `Literal` is aliased from `serde_json::Value`.
120    fn literal(
121        &self,
122        _oracle: &dyn CodeOracle,
123        _ctx: &dyn Display,
124        _renderer: &dyn LiteralRenderer,
125        literal: &Literal,
126    ) -> String {
127        match literal {
128            serde_json::Value::Number(v) => {
129                format!("{:.0}", v)
130            }
131            _ => unreachable!("Expecting a number"),
132        }
133    }
134
135    fn preference_getter(
136        &self,
137        _oracle: &dyn CodeOracle,
138        prefs: &dyn Display,
139        pref_key: &dyn Display,
140    ) -> Option<String> {
141        Some(format!("{prefs}.getInt({}, 0)", quoted(pref_key)))
142    }
143}
144
145pub(crate) struct StringCodeType;
146
147impl CodeType for StringCodeType {
148    /// The language specific label used to reference this type. This will be used in
149    /// method signatures and property declarations.
150    fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
151        "String".into()
152    }
153
154    fn property_getter(
155        &self,
156        oracle: &dyn CodeOracle,
157        vars: &dyn Display,
158        prop: &dyn Display,
159        default: &dyn Display,
160    ) -> String {
161        code_type::property_getter(self, oracle, vars, prop, default)
162    }
163
164    fn value_getter(
165        &self,
166        oracle: &dyn CodeOracle,
167        vars: &dyn Display,
168        prop: &dyn Display,
169    ) -> String {
170        code_type::value_getter(self, oracle, vars, prop)
171    }
172
173    fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
174        code_type::value_mapper(self, oracle)
175    }
176
177    /// The name of the type as it's represented in the `Variables` object.
178    /// The string return may be used to combine with an identifier, e.g. a `Variables` method name.
179    fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
180        VariablesType::String
181    }
182
183    /// A representation of the given literal for this type.
184    /// N.B. `Literal` is aliased from `serde_json::Value`.
185    fn literal(
186        &self,
187        _oracle: &dyn CodeOracle,
188        _ctx: &dyn Display,
189        _renderer: &dyn LiteralRenderer,
190        literal: &Literal,
191    ) -> String {
192        match literal {
193            serde_json::Value::String(v) => {
194                // Usually, we'd be wanting to escape this, for security reasons. However, this is
195                // will cause a kotlinc compile time error when the app is built if the string is malformed
196                // in the manifest.
197                quoted(v)
198            }
199            _ => unreachable!("Expecting a string"),
200        }
201    }
202
203    fn preference_getter(
204        &self,
205        _oracle: &dyn CodeOracle,
206        prefs: &dyn Display,
207        pref_key: &dyn Display,
208    ) -> Option<String> {
209        Some(format!("{prefs}.getString({}, \"\")", quoted(pref_key)))
210    }
211}
212
213#[cfg(test)]
214mod unit_tests {
215
216    use serde_json::json;
217
218    use crate::backends::TypeIdentifier;
219
220    use super::*;
221
222    struct TestCodeOracle;
223    impl CodeOracle for TestCodeOracle {
224        fn find(&self, _type_: &TypeIdentifier) -> Box<dyn CodeType> {
225            unreachable!()
226        }
227    }
228
229    struct TestRenderer;
230    impl LiteralRenderer for TestRenderer {
231        fn literal(
232            &self,
233            _oracle: &dyn CodeOracle,
234            _typ: &TypeIdentifier,
235            _value: &Literal,
236            _ctx: &dyn Display,
237        ) -> String {
238            unreachable!()
239        }
240    }
241
242    fn oracle() -> Box<dyn CodeOracle> {
243        Box::new(TestCodeOracle) as Box<dyn CodeOracle>
244    }
245
246    fn bool_type() -> Box<dyn CodeType> {
247        Box::new(BooleanCodeType) as Box<dyn CodeType>
248    }
249
250    fn string_type() -> Box<dyn CodeType> {
251        Box::new(StringCodeType) as Box<dyn CodeType>
252    }
253
254    fn int_type() -> Box<dyn CodeType> {
255        Box::new(IntCodeType) as Box<dyn CodeType>
256    }
257
258    #[test]
259    fn test_type_label() {
260        let oracle = &*oracle();
261
262        let ct = bool_type();
263        assert_eq!("Boolean".to_string(), ct.type_label(oracle));
264
265        let ct = string_type();
266        assert_eq!("String".to_string(), ct.type_label(oracle));
267
268        let ct = int_type();
269        assert_eq!("Int".to_string(), ct.type_label(oracle));
270    }
271
272    #[test]
273    fn test_literal() {
274        let oracle = &*oracle();
275        let finder = &TestRenderer;
276
277        let ct = bool_type();
278        let ctx = "context".to_string();
279        assert_eq!(
280            "true".to_string(),
281            ct.literal(oracle, &ctx, finder, &json!(true))
282        );
283        assert_eq!(
284            "false".to_string(),
285            ct.literal(oracle, &ctx, finder, &json!(false))
286        );
287
288        let ct = string_type();
289        assert_eq!(
290            r#""no""#.to_string(),
291            ct.literal(oracle, &ctx, finder, &json!("no"))
292        );
293        assert_eq!(
294            r#""yes""#.to_string(),
295            ct.literal(oracle, &ctx, finder, &json!("yes"))
296        );
297
298        let ct = int_type();
299        assert_eq!("1".to_string(), ct.literal(oracle, &ctx, finder, &json!(1)));
300        assert_eq!("2".to_string(), ct.literal(oracle, &ctx, finder, &json!(2)));
301    }
302
303    #[test]
304    fn test_get_value() {
305        let oracle = &*oracle();
306
307        let ct = bool_type();
308        assert_eq!(
309            r#"v.getBool("the-property")"#.to_string(),
310            ct.value_getter(oracle, &"v", &"the-property")
311        );
312
313        let ct = string_type();
314        assert_eq!(
315            r#"v.getString("the-property")"#.to_string(),
316            ct.value_getter(oracle, &"v", &"the-property")
317        );
318
319        let ct = int_type();
320        assert_eq!(
321            r#"v.getInt("the-property")"#.to_string(),
322            ct.value_getter(oracle, &"v", &"the-property")
323        );
324    }
325}