nimbus_fml/backends/kotlin/gen_structs/
enum_.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 askama::Template;
8
9use super::common;
10use super::common::code_type;
11use super::filters;
12use crate::backends::{CodeDeclaration, CodeOracle, CodeType, LiteralRenderer, VariablesType};
13use crate::intermediate_representation::{EnumDef, FeatureManifest, Literal};
14
15pub(crate) struct EnumCodeType {
16    id: String,
17}
18
19impl EnumCodeType {
20    pub(crate) fn new(id: String) -> Self {
21        Self { id }
22    }
23}
24
25impl CodeType for EnumCodeType {
26    /// The language specific label used to reference this type. This will be used in
27    /// method signatures and property declarations.
28    fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
29        common::class_name(&self.id)
30    }
31
32    fn property_getter(
33        &self,
34        oracle: &dyn CodeOracle,
35        vars: &dyn Display,
36        prop: &dyn Display,
37        default: &dyn Display,
38    ) -> String {
39        code_type::property_getter(self, oracle, vars, prop, default)
40    }
41
42    fn value_getter(
43        &self,
44        oracle: &dyn CodeOracle,
45        vars: &dyn Display,
46        prop: &dyn Display,
47    ) -> String {
48        code_type::value_getter(self, oracle, vars, prop)
49    }
50
51    fn value_mapper(&self, oracle: &dyn CodeOracle) -> Option<String> {
52        code_type::value_mapper(self, oracle)
53    }
54
55    /// The name of the type as it's represented in the `Variables` object.
56    /// The string return may be used to combine with an identifier, e.g. a `Variables` method name.
57    fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType {
58        VariablesType::String
59    }
60
61    /// A function handle that is capable of turning the variables type to the TypeRef type.
62    fn create_transform(&self, oracle: &dyn CodeOracle) -> Option<String> {
63        Some(format!(
64            "{enum_type}::enumValue",
65            enum_type = self.type_label(oracle)
66        ))
67    }
68
69    fn as_json_transform(&self, _oracle: &dyn CodeOracle, prop: &dyn Display) -> Option<String> {
70        Some(format!("{}.toJSONString()", prop))
71    }
72
73    /// A representation of the given literal for this type.
74    /// N.B. `Literal` is aliased from `serde_json::Value`.
75    fn literal(
76        &self,
77        oracle: &dyn CodeOracle,
78        _ctx: &dyn Display,
79        _renderer: &dyn LiteralRenderer,
80        literal: &Literal,
81    ) -> String {
82        let variant = match literal {
83            serde_json::Value::String(v) => v,
84            _ => unreachable!(),
85        };
86
87        format!(
88            "{}.{}",
89            self.type_label(oracle),
90            common::enum_variant_name(variant)
91        )
92    }
93}
94#[derive(Template)]
95#[template(syntax = "kt", escape = "none", path = "EnumTemplate.kt")]
96pub(crate) struct EnumCodeDeclaration {
97    inner: EnumDef,
98}
99
100impl EnumCodeDeclaration {
101    pub fn new(_fm: &FeatureManifest, inner: &EnumDef) -> Self {
102        Self {
103            inner: inner.clone(),
104        }
105    }
106    fn inner(&self) -> EnumDef {
107        self.inner.clone()
108    }
109}
110
111impl CodeDeclaration for EnumCodeDeclaration {
112    fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
113        Some(self.render().unwrap())
114    }
115}
116
117#[cfg(test)]
118mod unit_tests {
119
120    use serde_json::json;
121
122    use super::*;
123    use crate::backends::TypeIdentifier;
124
125    struct TestCodeOracle;
126    impl CodeOracle for TestCodeOracle {
127        fn find(&self, _type_: &TypeIdentifier) -> Box<dyn CodeType> {
128            unreachable!()
129        }
130    }
131
132    struct TestRenderer;
133    impl LiteralRenderer for TestRenderer {
134        fn literal(
135            &self,
136            _oracle: &dyn CodeOracle,
137            _typ: &TypeIdentifier,
138            _value: &Literal,
139            _ctx: &dyn Display,
140        ) -> String {
141            unreachable!()
142        }
143    }
144
145    fn oracle() -> Box<dyn CodeOracle> {
146        Box::new(TestCodeOracle) as Box<dyn CodeOracle>
147    }
148
149    fn code_type(name: &str) -> Box<dyn CodeType> {
150        Box::new(EnumCodeType::new(name.to_string())) as Box<dyn CodeType>
151    }
152
153    #[test]
154    fn test_type_label() {
155        let ct = code_type("AEnum");
156        let oracle = &*oracle();
157        assert_eq!("AEnum".to_string(), ct.type_label(oracle))
158    }
159
160    #[test]
161    fn test_literal() {
162        let ct = code_type("AEnum");
163        let oracle = &*oracle();
164        let finder = &TestRenderer;
165        let ctx = String::from("ctx");
166        assert_eq!(
167            "AEnum.FOO".to_string(),
168            ct.literal(oracle, &ctx, finder, &json!("foo"))
169        );
170        assert_eq!(
171            "AEnum.BAR_BAZ".to_string(),
172            ct.literal(oracle, &ctx, finder, &json!("barBaz"))
173        );
174        assert_eq!(
175            "AEnum.A_B_C".to_string(),
176            ct.literal(oracle, &ctx, finder, &json!("a-b-c"))
177        );
178    }
179
180    #[test]
181    fn test_get_value() {
182        let ct = code_type("AEnum");
183        let oracle = &*oracle();
184
185        assert_eq!(
186            r#"v.getString("the-property")"#.to_string(),
187            ct.value_getter(oracle, &"v", &"the-property")
188        );
189    }
190
191    #[test]
192    fn test_getter_with_fallback() {
193        let ct = code_type("AEnum");
194        let oracle = &*oracle();
195
196        assert_eq!(
197            r#"v.getString("the-property")?.let(AEnum::enumValue) ?: def"#.to_string(),
198            ct.property_getter(oracle, &"v", &"the-property", &"def")
199        );
200    }
201}