nimbus_fml/backends/kotlin/gen_structs/
mod.rs
1use askama::Template;
6use std::collections::HashSet;
7
8use crate::intermediate_representation::PropDef;
9use crate::{
10 backends::{CodeDeclaration, CodeOracle, CodeType, TypeIdentifier},
11 intermediate_representation::{FeatureDef, FeatureManifest, GeckoPrefDef, TypeFinder},
12};
13
14mod bundled;
15mod common;
16mod enum_;
17mod feature;
18mod filters;
19mod imports;
20mod object;
21mod primitives;
22mod structural;
23#[derive(Template)]
24#[template(syntax = "kt", escape = "none", path = "FeatureManifestTemplate.kt")]
25pub struct FeatureManifestDeclaration<'a> {
26 fm: &'a FeatureManifest,
27 oracle: ConcreteCodeOracle,
28}
29impl<'a> FeatureManifestDeclaration<'a> {
30 pub fn new(fm: &'a FeatureManifest) -> Self {
31 Self {
32 fm,
33 oracle: Default::default(),
34 }
35 }
36
37 pub fn members(&self) -> Vec<Box<dyn CodeDeclaration + 'a>> {
38 let fm = self.fm;
39
40 fm.iter_feature_defs()
41 .map(|inner| {
42 Box::new(feature::FeatureCodeDeclaration::new(fm, inner))
43 as Box<dyn CodeDeclaration>
44 })
45 .chain(fm.iter_enum_defs().map(|inner| {
46 Box::new(enum_::EnumCodeDeclaration::new(fm, inner)) as Box<dyn CodeDeclaration>
47 }))
48 .chain(fm.iter_object_defs().map(|inner| {
49 Box::new(object::ObjectCodeDeclaration::new(fm, inner)) as Box<dyn CodeDeclaration>
50 }))
51 .chain(fm.iter_imported_files().into_iter().map(|inner| {
52 Box::new(imports::ImportedModuleInitialization::new(inner))
53 as Box<dyn CodeDeclaration>
54 }))
55 .collect()
56 }
57
58 pub fn feature_properties(&self) -> Vec<PropDef> {
59 let fm = self.fm;
60
61 fm.iter_feature_defs()
62 .flat_map(|feature| feature.props())
63 .chain(
64 fm.iter_object_defs()
65 .flat_map(|object| object.props.clone()),
66 )
67 .chain(fm.iter_imported_files().into_iter().flat_map(|inner| {
68 inner
69 .fm
70 .iter_feature_defs()
71 .flat_map(|feature| feature.props())
72 }))
73 .collect()
74 }
75
76 pub fn iter_feature_defs(&self) -> Vec<&FeatureDef> {
77 self.fm.iter_feature_defs().collect::<_>()
78 }
79
80 #[allow(unused)]
81 pub fn iter_gecko_prefs(&self) -> Vec<&GeckoPrefDef> {
82 self.fm.iter_gecko_prefs().collect::<_>()
83 }
84
85 #[allow(unused)]
86 pub fn iter_features_with_prefs(&self) -> Vec<(String, Vec<(String, &GeckoPrefDef)>)> {
87 self.fm.iter_features_with_prefs().collect::<_>()
88 }
89
90 pub fn initialization_code(&self) -> Vec<String> {
91 let oracle = &self.oracle;
92 self.members()
93 .into_iter()
94 .filter_map(|member| member.initialization_code(oracle))
95 .collect()
96 }
97
98 pub fn declaration_code(&self) -> Vec<String> {
99 let oracle = &self.oracle;
100 self.members()
101 .into_iter()
102 .filter_map(|member| member.definition_code(oracle))
103 .collect()
104 }
105
106 pub fn imports(&self) -> Vec<String> {
107 let oracle = &self.oracle;
108 let my_package = format!(
110 "{}.*",
111 self.fm.about.nimbus_package_name().unwrap_or_default()
112 );
113 let mut imports: Vec<String> = self
114 .members()
115 .into_iter()
116 .filter_map(|member| member.imports(oracle))
117 .flatten()
118 .chain(
119 self.fm
120 .all_types()
121 .into_iter()
122 .filter_map(|type_| self.oracle.find(&type_).imports(oracle))
123 .flatten(),
124 )
125 .chain(vec![
126 "org.mozilla.experiments.nimbus.Variables".to_string(),
127 "org.mozilla.experiments.nimbus.internal.FeatureHolder".to_string(),
128 "org.mozilla.experiments.nimbus.internal.FeatureManifestInterface".to_string(),
129 "org.mozilla.experiments.nimbus.internal.GeckoPref".to_string(),
130 "org.mozilla.experiments.nimbus.FeaturesInterface".to_string(),
131 "org.json.JSONObject".to_string(),
132 "android.content.SharedPreferences".to_string(),
133 ])
134 .filter(|i| i != &my_package)
135 .collect::<HashSet<String>>()
136 .into_iter()
137 .collect();
138
139 let include_r: bool = self
140 .feature_properties()
141 .into_iter()
142 .any(|prop| self.oracle.find(&prop.typ()).is_resource_id(&prop.default));
143 if include_r {
144 imports.push(format!("{}.R", self.fm.about.resource_package_name()))
145 }
146
147 imports.sort();
148 imports
149 }
150}
151
152#[derive(Default, Clone)]
153pub struct ConcreteCodeOracle;
154
155impl ConcreteCodeOracle {
156 fn create_code_type(&self, type_: TypeIdentifier) -> Box<dyn CodeType> {
157 match type_ {
158 TypeIdentifier::Boolean => Box::new(primitives::BooleanCodeType),
159 TypeIdentifier::String | TypeIdentifier::StringAlias(_) => {
160 Box::new(primitives::StringCodeType)
161 }
162 TypeIdentifier::Int => Box::new(primitives::IntCodeType),
163
164 TypeIdentifier::BundleText => Box::new(bundled::TextCodeType),
165 TypeIdentifier::BundleImage => Box::new(bundled::ImageCodeType),
166
167 TypeIdentifier::Enum(id) => Box::new(enum_::EnumCodeType::new(id)),
168 TypeIdentifier::Object(id) => Box::new(object::ObjectCodeType::new(id)),
169
170 TypeIdentifier::Option(ref inner) => Box::new(structural::OptionalCodeType::new(inner)),
171 TypeIdentifier::List(ref inner) => Box::new(structural::ListCodeType::new(inner)),
172 TypeIdentifier::StringMap(ref v_type) => {
173 let k_type = &TypeIdentifier::String;
174 Box::new(structural::MapCodeType::new(k_type, v_type))
175 }
176 TypeIdentifier::EnumMap(ref k_type, ref v_type) => {
177 Box::new(structural::MapCodeType::new(k_type, v_type))
178 }
179 }
180 }
181}
182
183impl CodeOracle for ConcreteCodeOracle {
184 fn find(&self, type_: &TypeIdentifier) -> Box<dyn CodeType> {
185 self.create_code_type(type_.clone())
186 }
187}