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