1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "sysprop_rust_gen"
18
19 #include "RustGen.h"
20
21 #include <android-base/file.h>
22
23 #include <regex>
24 #include <string>
25
26 #include "CodeWriter.h"
27 #include "Common.h"
28 #include "sysprop.pb.h"
29
30 using android::base::Result;
31
32 namespace {
33
34 constexpr const char* kDocs = R"(//! Autogenerated system property accessors.
35 //!
36 //! This is an autogenerated module. The module contains methods for typed access to
37 //! Android system properties.)";
38
39 constexpr const char* kRustFileImports = R"(use std::fmt;
40 use rustutils::system_properties::{self, parsers_formatters};
41 pub use rustutils::system_properties::error::SysPropError;)";
42
43 constexpr const char* kIndent = " ";
44
45 constexpr const char* kDeprecated = "#[deprecated]";
46
GetRustEnumType(const sysprop::Property & prop)47 std::string GetRustEnumType(const sysprop::Property& prop) {
48 std::string result = ApiNameToIdentifier(prop.api_name());
49 return SnakeCaseToCamelCase(result) + "Values";
50 }
51
GetRustReturnType(const sysprop::Property & prop)52 std::string GetRustReturnType(const sysprop::Property& prop) {
53 switch (prop.type()) {
54 case sysprop::Boolean:
55 return "bool";
56 case sysprop::Integer:
57 return "i32";
58 case sysprop::UInt:
59 return "u32";
60 case sysprop::Long:
61 return "i64";
62 case sysprop::ULong:
63 return "u64";
64 case sysprop::Double:
65 return "f64";
66 case sysprop::String:
67 return "String";
68 case sysprop::Enum:
69 return GetRustEnumType(prop);
70 case sysprop::BooleanList:
71 return "Vec<bool>";
72 case sysprop::IntegerList:
73 return "Vec<i32>";
74 case sysprop::UIntList:
75 return "Vec<u32>";
76 case sysprop::LongList:
77 return "Vec<i64>";
78 case sysprop::ULongList:
79 return "Vec<u64>";
80 case sysprop::DoubleList:
81 return "Vec<f64>";
82 case sysprop::StringList:
83 return "Vec<String>";
84 case sysprop::EnumList:
85 return "Vec<" + GetRustEnumType(prop) + ">";
86 default:
87 __builtin_unreachable();
88 }
89 }
90
GetRustAcceptType(const sysprop::Property & prop)91 std::string GetRustAcceptType(const sysprop::Property& prop) {
92 switch (prop.type()) {
93 case sysprop::Boolean:
94 return "bool";
95 case sysprop::Integer:
96 return "i32";
97 case sysprop::UInt:
98 return "u32";
99 case sysprop::Long:
100 return "i64";
101 case sysprop::ULong:
102 return "u64";
103 case sysprop::Double:
104 return "f64";
105 case sysprop::String:
106 return "&str";
107 case sysprop::Enum:
108 return GetRustEnumType(prop);
109 case sysprop::BooleanList:
110 return "&[bool]";
111 case sysprop::IntegerList:
112 return "&[i32]";
113 case sysprop::UIntList:
114 return "&[u32]";
115 case sysprop::LongList:
116 return "&[i64]";
117 case sysprop::ULongList:
118 return "&[u64]";
119 case sysprop::DoubleList:
120 return "&[f64]";
121 case sysprop::StringList:
122 return "&[String]";
123 case sysprop::EnumList:
124 return "&[" + GetRustEnumType(prop) + "]";
125 default:
126 __builtin_unreachable();
127 }
128 }
129
GetTypeParser(const sysprop::Property & prop)130 std::string GetTypeParser(const sysprop::Property& prop) {
131 switch (prop.type()) {
132 case sysprop::Boolean:
133 return "parsers_formatters::parse_bool";
134 case sysprop::Integer:
135 case sysprop::UInt:
136 case sysprop::Long:
137 case sysprop::ULong:
138 case sysprop::Double:
139 case sysprop::String:
140 case sysprop::Enum:
141 return "parsers_formatters::parse";
142 case sysprop::BooleanList:
143 return "parsers_formatters::parse_bool_list";
144 case sysprop::IntegerList:
145 case sysprop::UIntList:
146 case sysprop::LongList:
147 case sysprop::ULongList:
148 case sysprop::DoubleList:
149 case sysprop::StringList:
150 case sysprop::EnumList:
151 return "parsers_formatters::parse_list";
152 default:
153 __builtin_unreachable();
154 }
155 }
156
GetTypeFormatter(const sysprop::Property & prop)157 std::string GetTypeFormatter(const sysprop::Property& prop) {
158 switch (prop.type()) {
159 case sysprop::Boolean:
160 if (prop.integer_as_bool()) {
161 return "parsers_formatters::format_bool_as_int";
162 }
163 return "parsers_formatters::format_bool";
164 case sysprop::String:
165 case sysprop::Integer:
166 case sysprop::UInt:
167 case sysprop::Long:
168 case sysprop::ULong:
169 case sysprop::Double:
170 case sysprop::Enum:
171 return "parsers_formatters::format";
172 case sysprop::BooleanList:
173 if (prop.integer_as_bool()) {
174 return "parsers_formatters::format_bool_list_as_int";
175 }
176 return "parsers_formatters::format_bool_list";
177 case sysprop::IntegerList:
178 case sysprop::UIntList:
179 case sysprop::LongList:
180 case sysprop::ULongList:
181 case sysprop::DoubleList:
182 case sysprop::StringList:
183 case sysprop::EnumList:
184 return "parsers_formatters::format_list";
185 default:
186 __builtin_unreachable();
187 }
188 }
189
GenerateRustSource(sysprop::Properties props,sysprop::Scope scope)190 std::string GenerateRustSource(sysprop::Properties props, sysprop::Scope scope) {
191 CodeWriter writer(kIndent);
192 writer.Write("%s\n\n", kDocs);
193 writer.Write("%s", kGeneratedFileFooterComments);
194 writer.Write("%s\n\n", kRustFileImports);
195
196 for (int i = 0; i < props.prop_size(); ++i) {
197 const sysprop::Property& prop = props.prop(i);
198 if (prop.scope() > scope) continue;
199
200 std::string prop_id =
201 CamelCaseToSnakeCase(ApiNameToIdentifier(prop.api_name()));
202
203 std::string prop_const = ToUpper(std::string(prop_id)) + "_PROP";
204
205 // Create constant.
206 writer.Write("/// The property name of the \"%s\" API.\n", prop_id.c_str());
207 writer.Write("pub const %s: &str = \"%s\";\n\n", prop_const.c_str(),
208 prop.prop_name().c_str());
209
210 // Create enum.
211 if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
212 auto enum_type = GetRustEnumType(prop);
213 auto values = ParseEnumValues(prop.enum_values());
214
215 writer.Write("#[allow(missing_docs)]\n");
216 writer.Write(
217 "#[derive(Copy, Clone, Debug, Eq, "
218 "PartialEq, PartialOrd, Hash, Ord)]\n");
219 writer.Write("pub enum %s {\n", enum_type.c_str());
220 writer.Indent();
221 for (const std::string& value : values) {
222 writer.Write("%s,\n", SnakeCaseToCamelCase(value).c_str());
223 }
224 writer.Dedent();
225 writer.Write("}\n\n");
226
227 // Enum parser.
228 writer.Write("impl std::str::FromStr for %s {\n", enum_type.c_str());
229 writer.Indent();
230 writer.Write("type Err = String;\n\n");
231 writer.Write(
232 "fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n");
233 writer.Indent();
234 writer.Write("match s {\n");
235 writer.Indent();
236 for (const std::string& value : values) {
237 writer.Write("\"%s\" => Ok(%s::%s),\n", value.c_str(),
238 enum_type.c_str(), SnakeCaseToCamelCase(value).c_str());
239 }
240 writer.Write("_ => Err(format!(\"'{}' cannot be parsed for %s\", s)),\n",
241 enum_type.c_str());
242 writer.Dedent();
243 writer.Write("}\n");
244 writer.Dedent();
245 writer.Write("}\n");
246 writer.Dedent();
247 writer.Write("}\n\n");
248
249 // Enum formatter.
250 writer.Write("impl fmt::Display for %s {\n", enum_type.c_str());
251 writer.Indent();
252 writer.Write(
253 "fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n");
254 writer.Indent();
255 writer.Write("match self {\n");
256 writer.Indent();
257 for (const std::string& value : values) {
258 writer.Write("%s::%s => write!(f, \"%s\"),\n", enum_type.c_str(),
259 SnakeCaseToCamelCase(value).c_str(), value.c_str());
260 }
261 writer.Dedent();
262 writer.Write("}\n");
263 writer.Dedent();
264 writer.Write("}\n");
265 writer.Dedent();
266 writer.Write("}\n\n");
267 }
268
269 // Write getter.
270 std::string prop_return_type = GetRustReturnType(prop);
271 std::string parser = GetTypeParser(prop);
272 writer.Write("/// Returns the value of the property '%s' if set.\n",
273 prop.prop_name().c_str());
274 if (prop.deprecated()) writer.Write("%s\n", kDeprecated);
275 // Escape prop id if it is similar to `type` keyword.
276 std::string identifier = (prop_id == "type") ? "r#" + prop_id : prop_id;
277 writer.Write(
278 "pub fn %s() -> std::result::Result<Option<%s>, SysPropError> {\n",
279 identifier.c_str(), prop_return_type.c_str());
280 writer.Indent();
281 // Try original property.
282 writer.Write("let result = match system_properties::read(%s) {\n",
283 prop_const.c_str());
284 writer.Indent();
285 writer.Write("Err(e) => Err(SysPropError::FetchError(e)),\n");
286 writer.Write(
287 "Ok(Some(val)) => "
288 "%s(val.as_str()).map_err(SysPropError::ParseError).map(Some),\n",
289 parser.c_str());
290 writer.Write("Ok(None) => Ok(None),\n");
291 writer.Dedent();
292 writer.Write("};\n");
293 // Try legacy property
294 if (!prop.legacy_prop_name().empty()) {
295 writer.Write("if result.is_ok() { return result; }\n");
296 // Avoid omitting the error when fallback to legacy.
297 writer.Write(
298 "log::debug!(\"Failed to fetch the original property '%s' ('{}'), "
299 "falling back to the legacy one '%s'.\", result.unwrap_err());\n",
300 prop.prop_name().c_str(), prop.legacy_prop_name().c_str());
301 writer.Write("match system_properties::read(\"%s\") {\n",
302 prop.legacy_prop_name().c_str());
303 writer.Indent();
304 writer.Write("Err(e) => Err(SysPropError::FetchError(e)),\n");
305 writer.Write(
306 "Ok(Some(val)) => "
307 "%s(val.as_str()).map_err(SysPropError::ParseError).map(Some),\n",
308 parser.c_str());
309 writer.Write("Ok(None) => Ok(None),\n");
310 writer.Dedent();
311 writer.Write("}\n");
312 } else {
313 writer.Write("result\n");
314 }
315 writer.Dedent();
316 writer.Write("}\n\n");
317
318 // Write setter.
319 if (prop.access() == sysprop::Readonly) continue;
320 std::string prop_accept_type = GetRustAcceptType(prop);
321 std::string formatter = GetTypeFormatter(prop);
322 writer.Write(
323 "/// Sets the value of the property '%s', "
324 "returns 'Ok' if successful.\n",
325 prop.prop_name().c_str());
326 if (prop.deprecated()) writer.Write("%s\n", kDeprecated);
327 writer.Write(
328 "pub fn set_%s(v: %s) -> std::result::Result<(), SysPropError> {\n",
329 prop_id.c_str(), prop_accept_type.c_str());
330 writer.Indent();
331 std::string write_arg;
332 if (prop.type() == sysprop::String) {
333 write_arg = "v";
334 } else {
335 // We need to borrow single values.
336 std::string format_arg = prop.type() >= 20 ? "v" : "&v";
337 writer.Write("let value = %s(%s);\n", formatter.c_str(),
338 format_arg.c_str());
339 write_arg = "value.as_str()";
340 }
341 writer.Write(
342 "system_properties::write(%s, %s).map_err(SysPropError::SetError)\n",
343 prop_const.c_str(), write_arg.c_str());
344 writer.Dedent();
345 writer.Write("}\n\n");
346 }
347 return writer.Code();
348 }
349
350 }; // namespace
351
GenerateRustLibrary(const std::string & input_file_path,sysprop::Scope scope,const std::string & rust_output_dir)352 Result<void> GenerateRustLibrary(const std::string& input_file_path,
353 sysprop::Scope scope,
354 const std::string& rust_output_dir) {
355 sysprop::Properties props;
356
357 if (auto res = ParseProps(input_file_path); res.ok()) {
358 props = std::move(*res);
359 } else {
360 return res.error();
361 }
362
363 std::string lib_path = rust_output_dir + "/mod.rs";
364 std::string lib_result = GenerateRustSource(props, scope);
365 if (!android::base::WriteStringToFile(lib_result, lib_path)) {
366 return ErrnoErrorf("Writing generated rust lib to {} failed", lib_path);
367 }
368
369 return {};
370 }
371