nimbus_fml/backends/
mod.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
5//! # Backend traits
6//!
7//! This module provides a number of traits useful for implementing a backend for FML structs.
8//!
9//! A [CodeType] is needed for each type that is referred to in the feature definition (i.e. every [TypeRef]
10//! instance should have corresponding `CodeType` instance). Helper code for types might include managing how merging/overriding of
11//! defaults occur.
12//!
13//! A [CodeDeclaration] is needed for each type that is declared in the manifest file: i.e. an Object classes, Enum classes and Feature classes.
14//! This has access to intermediate structs of the [crate::intermediate_representation::FeatureManifest] so may want to do some additional lookups to help rendering.
15//!
16//! `CodeDeclaration`s provide the target language's version of the type defined in the feature manifest. For objects and features, this would
17//! be objects that have properties corresponding to the FML variables. For enums, this would mean the Enum class definition. In all cases, this will
18//! likely be attached to an [askama::Template].
19//!
20//! `CodeDeclaration`s can also be used to conditionally include code: e.g. only include the CallbackInterfaceRuntime
21//! if the user has used at least one callback interface.
22//!
23//! Each backend has a wrapper template for each file it needs to generate. This should collect the `CodeDeclaration`s that
24//! the backend and `FeatureManifest` between them specify and use them to stitch together a file in the target language.
25//!
26//! The [CodeOracle] provides methods to map the `TypeRef` values found in the `FeatureManifest` to the `CodeType`s specified
27//! by the backend.
28//!
29//! Each backend will have its own `filter` module, which is used by the askama templates used in all `CodeType`s and `CodeDeclaration`s.
30//! This filter provides methods to generate expressions and identifiers in the target language. These are all forwarded to the oracle.
31
32use std::fmt::Display;
33
34use crate::intermediate_representation::Literal;
35use crate::intermediate_representation::TypeRef;
36
37pub type TypeIdentifier = TypeRef;
38
39/// An object to look up a foreign language code specific renderer for a given type used.
40/// Every [TypeRef] referred to in the [crate::intermediate_representation::FeatureManifest] should map to a corresponding
41/// `CodeType`.
42///
43/// The mapping may be opaque, but the oracle always knows the answer.
44pub trait CodeOracle {
45    fn find(&self, type_: &TypeIdentifier) -> Box<dyn CodeType>;
46}
47
48/// A Trait to emit foreign language code to handle referenced types.
49/// A type which is specified in the FML (i.e. a type that a variable declares itself of)
50/// will have a `CodeDeclaration` as well, but for types used e.g. primitive types, Strings, etc
51/// only a `CodeType` is needed.
52///
53/// This includes generating an literal of the type from the right type of JSON and
54/// expressions to get a property from the JSON backed `Variables` object.
55pub trait CodeType {
56    /// The language specific label used to reference this type. This will be used in
57    /// method signatures and property declarations.
58    fn type_label(&self, oracle: &dyn CodeOracle) -> String;
59
60    /// The language specific expression that gets a value of the `prop` from the `vars` object,
61    /// and fallbacks to the `default` value.
62    ///
63    /// /// All the propertis follow this general pattern:
64    ///
65    /// ```kt
66    /// variables?.{{ value_getter }}
67    ///         ?.{{ value_mapper }}
68    ///         ?.{{ value_merger }}
69    ///         ?: {{ default_fallback}}
70    /// ```
71    ///
72    /// In the case of structural types and objects, `value_mapper` and `value_merger`
73    /// become mutually recursive to generate quite complicated properties.
74    ///
75    fn property_getter(
76        &self,
77        oracle: &dyn CodeOracle,
78        vars: &dyn Display,
79        prop: &dyn Display,
80        default: &dyn Display,
81    ) -> String;
82
83    /// The expression needed to get a value out of a `Variables` objectm with the `prop` key.
84    ///
85    /// This will almost certainly use the `variables_type` method to determine which method to use.
86    /// e.g. `vars?.getString("prop")`
87    ///
88    /// The `value_mapper` will be used to transform this value into the required value.
89    fn value_getter(
90        &self,
91        oracle: &dyn CodeOracle,
92        vars: &dyn Display,
93        prop: &dyn Display,
94    ) -> String;
95
96    /// The method call here will use the `create_transform` to transform the value coming out of
97    /// the `Variables` object into the desired type.
98    ///
99    /// e.g. a string will need to be transformed into an enum, so the value mapper in Kotlin will be
100    /// `let(Enum::enumValue)`.
101    ///
102    /// If the value is `None`, then no mapper is used.
103    fn value_mapper(&self, _oracle: &dyn CodeOracle) -> Option<String> {
104        None
105    }
106
107    /// The method call to merge the value with the defaults.
108    ///
109    /// This may use the `merge_transform`.
110    ///
111    /// If this returns `None`, no merging happens, and implicit `null` replacement happens.
112    fn value_merger(&self, _oracle: &dyn CodeOracle, _default: &dyn Display) -> Option<String> {
113        None
114    }
115
116    /// The name of the type as it's represented in the `Variables` object.
117    /// The string return may be used to combine with an identifier, e.g. a `Variables` method name.
118    fn variables_type(&self, _oracle: &dyn CodeOracle) -> VariablesType;
119
120    /// A function handle that is capable of turning the variables type to the TypeRef type.
121    fn create_transform(&self, _oracle: &dyn CodeOracle) -> Option<String> {
122        None
123    }
124
125    /// A function handle that is capable of merging two instances of the same class. By default, this is None.
126    fn merge_transform(&self, _oracle: &dyn CodeOracle) -> Option<String> {
127        None
128    }
129
130    // The foreign language type for how default values are stored in the `Defaults` object.
131    // This is usually the same as the type_label itself, but occasionally— e.g. for bundled resources—
132    // this will be different.
133    // If it is different, then a `defaults_mapper` is needed to map between the `defaults_type` and the
134    // `type_label` type.
135    fn defaults_type(&self, oracle: &dyn CodeOracle) -> String {
136        self.type_label(oracle)
137    }
138
139    fn defaults_mapper(
140        &self,
141        _oracle: &dyn CodeOracle,
142        _value: &dyn Display,
143        _vars: &dyn Display,
144    ) -> Option<String> {
145        None
146    }
147
148    fn preference_getter(
149        &self,
150        _oracle: &dyn CodeOracle,
151        _prefs: &dyn Display,
152        _pref_key: &dyn Display,
153    ) -> Option<String> {
154        None
155    }
156
157    /// Call from the template
158    fn as_json(&self, oracle: &dyn CodeOracle, prop: &dyn Display) -> String {
159        self.as_json_transform(oracle, prop)
160            .unwrap_or_else(|| prop.to_string())
161    }
162
163    /// Implement these in different code types, and call recursively from different code types.
164    fn as_json_transform(&self, _oracle: &dyn CodeOracle, _prop: &dyn Display) -> Option<String> {
165        None
166    }
167
168    /// A representation of the given literal for this type.
169    /// N.B. `Literal` is aliased from `serde_json::Value`.
170    fn literal(
171        &self,
172        oracle: &dyn CodeOracle,
173        ctx: &dyn Display,
174        renderer: &dyn LiteralRenderer,
175        literal: &Literal,
176    ) -> String;
177
178    fn is_resource_id(&self, _literal: &Literal) -> bool {
179        false
180    }
181
182    /// Optional helper code to make this type work.
183    /// This might include functions to patch a default value with another.
184    #[allow(dead_code)]
185    fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
186        None
187    }
188
189    /// A list of imports that are needed if this type is in use.
190    /// Classes are imported exactly once.
191    fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
192        None
193    }
194}
195
196pub trait LiteralRenderer {
197    fn literal(
198        &self,
199        _oracle: &dyn CodeOracle,
200        _typ: &TypeIdentifier,
201        value: &Literal,
202        ctx: &dyn Display,
203    ) -> String;
204}
205
206impl<T, C> LiteralRenderer for T
207where
208    T: std::ops::Deref<Target = C>,
209    C: LiteralRenderer,
210{
211    fn literal(
212        &self,
213        oracle: &dyn CodeOracle,
214        typ: &TypeIdentifier,
215        value: &Literal,
216        ctx: &dyn Display,
217    ) -> String {
218        self.deref().literal(oracle, typ, value, ctx)
219    }
220}
221
222/// A trait that is able to render a declaration about a particular member declared in
223/// the `FeatureManifest`.
224/// Like `CodeType`, it can render declaration code and imports.
225/// All methods are optional, and there is no requirement that the trait be used for a particular
226/// member. Thus, it can also be useful for conditionally rendering code.
227pub trait CodeDeclaration {
228    /// A list of imports that are needed if this type is in use.
229    /// Classes are imported exactly once.
230    fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
231        None
232    }
233
234    /// Code (one or more statements) that is run on start-up of the library,
235    /// but before the client code has access to it.
236    fn initialization_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
237        None
238    }
239
240    /// Code which represents this member. e.g. the foreign language class definition for
241    /// a given Object type.
242    fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
243        None
244    }
245}
246
247/// The generated code is running against hand written code to give type safe, error free access to JSON.
248/// This is the `Variables` object. This enum gives the underlying types that the `Variables` object supports.
249pub enum VariablesType {
250    Bool,
251    Image,
252    Int,
253    String,
254    Text,
255    Variables,
256}
257
258/// The Variables objects use a naming convention to name its methods. e.g. `getBool`, `getBoolList`, `getBoolMap`.
259/// In part this is to make generating code easier.
260/// This is the mapping from type to identifier part that corresponds to its type.
261impl Display for VariablesType {
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263        let nm = match self {
264            VariablesType::Bool => "Bool",
265            VariablesType::Image => "Image",
266            VariablesType::Int => "Int",
267            VariablesType::String => "String",
268            VariablesType::Text => "Text",
269            VariablesType::Variables => "Variables",
270        };
271        f.write_str(nm)
272    }
273}
274
275pub(crate) mod experimenter_manifest;
276pub(crate) mod frontend_manifest;
277pub(crate) mod info;
278pub(crate) mod kotlin;
279pub(crate) mod swift;