• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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