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