1 /*
2 * Copyright (C) 2021, 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 #include "rust_writer.h"
18
19 #include <stdio.h>
20
21 #include <algorithm>
22 #include <cctype>
23 #include <map>
24
25 #include "Collation.h"
26 #include "utils.h"
27
28 // Note that we prepend _ to variable names to avoid using Rust language keyword.
29 // E.g., a variable named "type" would not compile.
30
31 namespace android {
32 namespace stats_log_api_gen {
33
rust_type_name(java_type_t type,bool lifetime)34 const char* rust_type_name(java_type_t type, bool lifetime) {
35 switch (type) {
36 case JAVA_TYPE_BOOLEAN:
37 return "bool";
38 case JAVA_TYPE_INT:
39 case JAVA_TYPE_ENUM:
40 return "i32";
41 case JAVA_TYPE_LONG:
42 return "i64";
43 case JAVA_TYPE_FLOAT:
44 return "f32";
45 case JAVA_TYPE_DOUBLE:
46 return "f64";
47 case JAVA_TYPE_STRING:
48 if (lifetime) {
49 return "&'a str";
50 } else {
51 return "&str";
52 }
53 case JAVA_TYPE_BYTE_ARRAY:
54 if (lifetime) {
55 return "&'a [u8]";
56 } else {
57 return "&[u8]";
58 }
59 default:
60 return "UNKNOWN";
61 }
62 }
63
make_camel_case_name(const string & str)64 static string make_camel_case_name(const string& str) {
65 string result;
66 const int N = str.size();
67 bool justSawUnderscore = false;
68 for (int i = 0; i < N; i++) {
69 const char c = str[i];
70 if (c == '_') {
71 justSawUnderscore = true;
72 // Don't add the underscore to our result
73 } else if (i == 0 || justSawUnderscore) {
74 result += toupper(c);
75 justSawUnderscore = false;
76 } else {
77 result += tolower(c);
78 }
79 }
80 return result;
81 }
82
make_snake_case_name(const string & str)83 static string make_snake_case_name(const string& str) {
84 string result;
85 const int N = str.size();
86 for (int i = 0; i < N; i++) {
87 const char c = str[i];
88 if (isupper(c)) {
89 if (i > 0) {
90 result += "_";
91 }
92 result += tolower(c);
93 } else {
94 result += c;
95 }
96 }
97 return result;
98 }
99
get_variable_name(const string & str)100 static string get_variable_name(const string& str) {
101 // From https://doc.rust-lang.org/reference/identifiers.html.
102 if (str == "crate" || str == "self" || str == "super" || str == "Self") {
103 return make_snake_case_name(str) + "_";
104 } else {
105 return "r#" + make_snake_case_name(str);
106 }
107 }
108
write_rust_method_signature(FILE * out,const char * namePrefix,const AtomDecl & atomDecl,const AtomDecl & attributionDecl,bool isCode,bool isNonChained,const char * headerCrate)109 static void write_rust_method_signature(FILE* out, const char* namePrefix, const AtomDecl& atomDecl,
110 const AtomDecl& attributionDecl, bool isCode,
111 bool isNonChained, const char* headerCrate) {
112 // To make the generated code pretty, add newlines between arguments.
113 const char* separator = (isCode ? "\n" : " ");
114 if (isCode) {
115 fprintf(out, " pub fn %s(", namePrefix);
116 } else {
117 fprintf(out, " %s::%s(", atomDecl.name.c_str(), namePrefix);
118 }
119 if (isCode) {
120 fprintf(out, "\n");
121 }
122 if (atomDecl.atomType == ATOM_TYPE_PULLED) {
123 if (isCode) {
124 fprintf(out, " ");
125 }
126 fprintf(out, "pulled_data_: &mut AStatsEventList,%s", separator);
127 }
128 for (int i = 0; i < atomDecl.fields.size(); i++) {
129 const AtomField& atomField = atomDecl.fields[i];
130 const java_type_t& type = atomField.javaType;
131 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
132 for (int j = 0; j < attributionDecl.fields.size(); j++) {
133 const AtomField& chainField = attributionDecl.fields[j];
134 if (isCode) {
135 fprintf(out, " ");
136 }
137 fprintf(out, "%s_chain_: &[%s],%s", chainField.name.c_str(),
138 rust_type_name(chainField.javaType, false), separator);
139 }
140 } else {
141 if (isCode) {
142 fprintf(out, " ");
143 }
144 // Other arguments can have the same name as these non-chained ones,
145 // so append something.
146 if (isNonChained && i < 2) {
147 fprintf(out, "%s_non_chained_", atomField.name.c_str());
148 } else {
149 fprintf(out, "%s", get_variable_name(atomField.name).c_str());
150 }
151 if (type == JAVA_TYPE_ENUM) {
152 fprintf(out, ": %s,%s", make_camel_case_name(atomField.name).c_str(), separator);
153 } else {
154 fprintf(out, ": %s,%s", rust_type_name(type, false), separator);
155 }
156 }
157 }
158 fprintf(out, " ) -> %s::StatsResult", headerCrate);
159 if (isCode) {
160 fprintf(out, " {");
161 }
162 fprintf(out, "\n");
163 }
164
write_rust_usage(FILE * out,const string & method_name,const shared_ptr<AtomDecl> & atom,const AtomDecl & attributionDecl,bool isNonChained,const char * headerCrate)165 static bool write_rust_usage(FILE* out, const string& method_name, const shared_ptr<AtomDecl>& atom,
166 const AtomDecl& attributionDecl, bool isNonChained,
167 const char* headerCrate) {
168 fprintf(out, " // Definition: ");
169 write_rust_method_signature(out, method_name.c_str(), *atom, attributionDecl, false,
170 isNonChained, headerCrate);
171 return true;
172 }
173
write_rust_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const char * headerCrate)174 static void write_rust_atom_constants(FILE* out, const Atoms& atoms,
175 const AtomDecl& attributionDecl, const char* headerCrate) {
176 fprintf(out, "// Constants for atom codes.\n");
177 fprintf(out, "#[derive(Clone, Copy)]\n");
178 fprintf(out, "pub enum Atoms {\n");
179
180 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
181 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
182
183 for (const shared_ptr<AtomDecl>& atomDecl : atoms.decls) {
184 const string constant = make_camel_case_name(atomDecl->name);
185 fprintf(out, "\n");
186 fprintf(out, " // %s %s\n", atomDecl->message.c_str(), atomDecl->name.c_str());
187 const bool isSupported = write_rust_usage(out, "// stats_write", atomDecl, attributionDecl,
188 false, headerCrate);
189 if (!isSupported) {
190 continue;
191 }
192
193 auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atomDecl->code);
194 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
195 write_rust_usage(out, "stats_write_non_chained", *non_chained_decl->second,
196 attributionDecl, true, headerCrate);
197 }
198 fprintf(out, " %s = %d,\n", constant.c_str(), atomDecl->code);
199 }
200
201 fprintf(out, "\n");
202 fprintf(out, "}\n");
203 fprintf(out, "\n");
204 }
205
write_rust_atom_constant_values(FILE * out,const shared_ptr<AtomDecl> & atomDecl)206 static void write_rust_atom_constant_values(FILE* out, const shared_ptr<AtomDecl>& atomDecl) {
207 bool hasConstants = false;
208 for (const AtomField& field : atomDecl->fields) {
209 if (field.javaType == JAVA_TYPE_ENUM) {
210 fprintf(out, " #[repr(i32)]\n");
211 fprintf(out, " #[derive(Clone, Copy, Eq, PartialEq)]\n");
212 fprintf(out, " pub enum %s {\n", make_camel_case_name(field.name).c_str());
213 for (map<int, string>::const_iterator value = field.enumValues.begin();
214 value != field.enumValues.end(); value++) {
215 fprintf(out, " %s = %d,\n", make_camel_case_name(value->second).c_str(),
216 value->first);
217 }
218 fprintf(out, " }\n");
219 hasConstants = true;
220 }
221 }
222 if (hasConstants) {
223 fprintf(out, "\n");
224 }
225 }
226
write_rust_annotation_constants(FILE * out)227 static void write_rust_annotation_constants(FILE* out) {
228 fprintf(out, "// Annotation constants.\n");
229 // The annotation names are all prefixed with AnnotationId.
230 // Ideally that would go into the enum name instead,
231 // but I don't want to modify the actual name strings in case they change in the future.
232 fprintf(out, "#[allow(unused, clippy::enum_variant_names)]\n");
233 fprintf(out, "#[repr(u8)]\n");
234 fprintf(out, "enum Annotations {\n");
235
236 const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
237 get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
238 for (const auto& [id, annotation] : ANNOTATION_ID_CONSTANTS) {
239 fprintf(out, " %s = %hhu,\n", make_camel_case_name(annotation.name).c_str(), id);
240 }
241 fprintf(out, "}\n\n");
242 }
243
244 // This is mostly copied from the version in native_writer with some minor changes.
245 // Note that argIndex is 1 for the first argument.
write_annotations(FILE * out,int argIndex,const AtomDecl & atomDecl,const string & methodPrefix,const string & methodSuffix)246 static void write_annotations(FILE* out, int argIndex, const AtomDecl& atomDecl,
247 const string& methodPrefix, const string& methodSuffix) {
248 const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
249 get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
250 auto annotationsIt = atomDecl.fieldNumberToAnnotations.find(argIndex);
251 if (annotationsIt == atomDecl.fieldNumberToAnnotations.end()) {
252 return;
253 }
254 int resetState = -1;
255 int defaultState = -1;
256 for (const shared_ptr<Annotation>& annotation : annotationsIt->second) {
257 const string& annotationConstant =
258 ANNOTATION_ID_CONSTANTS.at(annotation->annotationId).name;
259 switch (annotation->type) {
260 case ANNOTATION_TYPE_INT:
261 if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
262 resetState = annotation->value.intValue;
263 } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
264 defaultState = annotation->value.intValue;
265 } else {
266 fprintf(out,
267 " %saddInt32Annotation(%scrate::Annotations::%s as u8, "
268 "%d);\n",
269 methodPrefix.c_str(), methodSuffix.c_str(),
270 make_camel_case_name(annotationConstant).c_str(),
271 annotation->value.intValue);
272 }
273 break;
274 case ANNOTATION_TYPE_BOOL:
275 fprintf(out,
276 " %saddBoolAnnotation(%scrate::Annotations::%s as u8, %s);\n",
277 methodPrefix.c_str(), methodSuffix.c_str(),
278 make_camel_case_name(annotationConstant).c_str(),
279 annotation->value.boolValue ? "true" : "false");
280 break;
281 default:
282 break;
283 }
284 }
285 if (defaultState != -1 && resetState != -1) {
286 const string& annotationConstant =
287 ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET).name;
288 const AtomField& field = atomDecl.fields[argIndex - 1];
289 if (field.javaType == JAVA_TYPE_ENUM) {
290 fprintf(out, " if %s as i32 == %d {\n",
291 get_variable_name(field.name).c_str(), resetState);
292 } else {
293 fprintf(out, " if %s == %d {\n", get_variable_name(field.name).c_str(),
294 resetState);
295 }
296 fprintf(out, " %saddInt32Annotation(%scrate::Annotations::%s as u8, %d);\n",
297 methodPrefix.c_str(), methodSuffix.c_str(),
298 make_camel_case_name(annotationConstant).c_str(), defaultState);
299 fprintf(out, " }\n");
300 }
301 }
302
write_rust_method_body(FILE * out,const AtomDecl & atomDecl,const AtomDecl & attributionDecl,const int minApiLevel,const char * headerCrate)303 static int write_rust_method_body(FILE* out, const AtomDecl& atomDecl,
304 const AtomDecl& attributionDecl, const int minApiLevel,
305 const char* headerCrate) {
306 fprintf(out, " unsafe {\n");
307 if (minApiLevel == API_Q) {
308 fprintf(stderr, "TODO: Do we need to handle this case?");
309 return 1;
310 }
311 if (atomDecl.atomType == ATOM_TYPE_PUSHED) {
312 fprintf(out, " let __event = AStatsEvent_obtain();\n");
313 fprintf(out, " let __dropper = crate::AStatsEventDropper(__event);\n");
314 } else {
315 fprintf(out, " let __event = AStatsEventList_addStatsEvent(pulled_data_);\n");
316 }
317 fprintf(out, " AStatsEvent_setAtomId(__event, %s::Atoms::%s as u32);\n", headerCrate,
318 make_camel_case_name(atomDecl.name).c_str());
319 write_annotations(out, ATOM_ID_FIELD_NUMBER, atomDecl, "AStatsEvent_", "__event, ");
320 for (int i = 0; i < atomDecl.fields.size(); i++) {
321 const AtomField& atomField = atomDecl.fields[i];
322 const string& name = get_variable_name(atomField.name);
323 const java_type_t& type = atomField.javaType;
324 switch (type) {
325 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
326 const char* uidName = attributionDecl.fields.front().name.c_str();
327 const char* tagName = attributionDecl.fields.back().name.c_str();
328 fprintf(out,
329 " let uids = %s_chain_.iter().map(|n| (*n).try_into())"
330 ".collect::<std::result::Result<Vec<_>, _>>()?;\n"
331 " let str_arr = %s_chain_.iter().map(|s| "
332 "std::ffi::CString::new(*s))"
333 ".collect::<std::result::Result<Vec<_>, _>>()?;\n"
334 " let ptr_arr = str_arr.iter().map(|s| s.as_ptr())"
335 ".collect::<std::vec::Vec<_>>();\n"
336 " AStatsEvent_writeAttributionChain(__event, uids.as_ptr(),\n"
337 " ptr_arr.as_ptr(), %s_chain_.len().try_into()?);\n",
338 uidName, tagName, uidName);
339 break;
340 }
341 case JAVA_TYPE_BYTE_ARRAY:
342 fprintf(out,
343 " AStatsEvent_writeByteArray(__event, "
344 "%s.as_ptr(), %s.len());\n",
345 name.c_str(), name.c_str());
346 break;
347 case JAVA_TYPE_BOOLEAN:
348 fprintf(out, " AStatsEvent_writeBool(__event, %s);\n", name.c_str());
349 break;
350 case JAVA_TYPE_ENUM:
351 fprintf(out, " AStatsEvent_writeInt32(__event, %s as i32);\n",
352 name.c_str());
353 break;
354 case JAVA_TYPE_INT:
355 fprintf(out, " AStatsEvent_writeInt32(__event, %s);\n", name.c_str());
356 break;
357 case JAVA_TYPE_FLOAT:
358 fprintf(out, " AStatsEvent_writeFloat(__event, %s);\n", name.c_str());
359 break;
360 case JAVA_TYPE_LONG:
361 fprintf(out, " AStatsEvent_writeInt64(__event, %s);\n", name.c_str());
362 break;
363 case JAVA_TYPE_STRING:
364 fprintf(out, " let str = std::ffi::CString::new(%s)?;\n", name.c_str());
365 fprintf(out, " AStatsEvent_writeString(__event, str.as_ptr());\n");
366 break;
367 default:
368 // Unsupported types: OBJECT, DOUBLE
369 fprintf(stderr, "Encountered unsupported type: %d.", type);
370 return 1;
371 }
372 // write_annotations expects the first argument to have an index of 1.
373 write_annotations(out, i + 1, atomDecl, "AStatsEvent_", "__event, ");
374 }
375 if (atomDecl.atomType == ATOM_TYPE_PUSHED) {
376 fprintf(out, " let __ret = AStatsEvent_write(__event);\n");
377 fprintf(out,
378 " if __ret >= 0 { %s::StatsResult::Ok(()) }"
379 " else { Err(%s::StatsError::Return(__ret)) }\n",
380 headerCrate, headerCrate);
381 } else {
382 fprintf(out, " AStatsEvent_build(__event);\n");
383 fprintf(out, " %s::StatsResult::Ok(())\n", headerCrate);
384 }
385 fprintf(out, " }\n");
386 return 0;
387 }
388
write_rust_stats_write_method(FILE * out,const shared_ptr<AtomDecl> & atomDecl,const AtomDecl & attributionDecl,const int minApiLevel,const char * headerCrate)389 static int write_rust_stats_write_method(FILE* out, const shared_ptr<AtomDecl>& atomDecl,
390 const AtomDecl& attributionDecl, const int minApiLevel,
391 const char* headerCrate) {
392 if (atomDecl->atomType == ATOM_TYPE_PUSHED) {
393 write_rust_method_signature(out, "stats_write", *atomDecl, attributionDecl, true, false,
394 headerCrate);
395 } else {
396 write_rust_method_signature(out, "add_astats_event", *atomDecl, attributionDecl, true,
397 false, headerCrate);
398 }
399 const int ret =
400 write_rust_method_body(out, *atomDecl, attributionDecl, minApiLevel, headerCrate);
401 if (ret != 0) {
402 return ret;
403 }
404 fprintf(out, " }\n\n");
405 return 0;
406 }
407
write_rust_stats_write_non_chained_method(FILE * out,const shared_ptr<AtomDecl> & atomDecl,const AtomDecl & attributionDecl,const char * headerCrate)408 static void write_rust_stats_write_non_chained_method(FILE* out,
409 const shared_ptr<AtomDecl>& atomDecl,
410 const AtomDecl& attributionDecl,
411 const char* headerCrate) {
412 write_rust_method_signature(out, "stats_write_non_chained", *atomDecl, attributionDecl, true,
413 true, headerCrate);
414 fprintf(out, " stats_write(");
415 for (int i = 0; i < atomDecl->fields.size(); i++) {
416 if (i != 0) {
417 fprintf(out, ", ");
418 }
419 const AtomField& atomField = atomDecl->fields[i];
420 const java_type_t& type = atomField.javaType;
421 if (i < 2) {
422 // The first two args are attribution chains.
423 fprintf(out, "&[%s_non_chained_]", atomField.name.c_str());
424 } else if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
425 for (int j = 0; j < attributionDecl.fields.size(); j++) {
426 const AtomField& chainField = attributionDecl.fields[j];
427 if (i != 0 || j != 0) {
428 fprintf(out, ", ");
429 }
430 fprintf(out, "&[%s_chain_]", chainField.name.c_str());
431 }
432 } else {
433 fprintf(out, "%s", get_variable_name(atomField.name).c_str());
434 }
435 }
436 fprintf(out, ")\n");
437 fprintf(out, " }\n\n");
438 }
439
needs_lifetime(const shared_ptr<AtomDecl> & atomDecl)440 static bool needs_lifetime(const shared_ptr<AtomDecl>& atomDecl) {
441 for (const AtomField& atomField : atomDecl->fields) {
442 const java_type_t& type = atomField.javaType;
443 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN || type == JAVA_TYPE_STRING ||
444 type == JAVA_TYPE_BYTE_ARRAY) {
445 return true;
446 }
447 }
448 return false;
449 }
450
write_rust_struct(FILE * out,const shared_ptr<AtomDecl> & atomDecl,const AtomDecl & attributionDecl,const char * headerCrate)451 static void write_rust_struct(FILE* out, const shared_ptr<AtomDecl>& atomDecl,
452 const AtomDecl& attributionDecl, const char* headerCrate) {
453 // Write the struct.
454 const bool lifetime = needs_lifetime(atomDecl);
455 if (lifetime) {
456 fprintf(out, " pub struct %s<'a> {\n", make_camel_case_name(atomDecl->name).c_str());
457 } else {
458 fprintf(out, " pub struct %s {\n", make_camel_case_name(atomDecl->name).c_str());
459 }
460 for (const AtomField& atomField : atomDecl->fields) {
461 const java_type_t& type = atomField.javaType;
462 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
463 for (const AtomField& chainField : attributionDecl.fields) {
464 fprintf(out, " pub %s_chain_: &'a [%s],\n", chainField.name.c_str(),
465 rust_type_name(chainField.javaType, true));
466 }
467 } else {
468 fprintf(out, " pub %s:", get_variable_name(atomField.name).c_str());
469 if (type == JAVA_TYPE_ENUM) {
470 fprintf(out, " %s,\n", make_camel_case_name(atomField.name).c_str());
471 } else {
472 fprintf(out, " %s,\n", rust_type_name(type, true));
473 }
474 }
475 }
476 fprintf(out, " }\n");
477
478 // Write the impl
479 const bool isPush = atomDecl->atomType == ATOM_TYPE_PUSHED;
480 if (isPush) {
481 if (lifetime) {
482 fprintf(out, " impl<'a> %s<'a> {\n", make_camel_case_name(atomDecl->name).c_str());
483 } else {
484 fprintf(out, " impl %s {\n", make_camel_case_name(atomDecl->name).c_str());
485 }
486 } else {
487 if (lifetime) {
488 fprintf(out, " impl<'a> %s::Stat for %s<'a> {\n", headerCrate,
489 make_camel_case_name(atomDecl->name).c_str());
490 } else {
491 fprintf(out, " impl %s::Stat for %s {\n", headerCrate,
492 make_camel_case_name(atomDecl->name).c_str());
493 }
494 }
495 fprintf(out, " #[inline(always)]\n");
496 if (isPush) {
497 fprintf(out,
498 " pub fn stats_write(&self)"
499 " -> %s::StatsResult {\n",
500 headerCrate);
501 fprintf(out, " stats_write(");
502 } else {
503 fprintf(out,
504 " fn add_astats_event(&self, pulled_data: &mut AStatsEventList)"
505 " -> %s::StatsResult {\n",
506 headerCrate);
507 fprintf(out, " add_astats_event(pulled_data, ");
508 }
509 for (const AtomField& atomField : atomDecl->fields) {
510 const java_type_t& type = atomField.javaType;
511 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
512 for (const AtomField& chainField : attributionDecl.fields) {
513 fprintf(out, "self.%s_chain_, ", chainField.name.c_str());
514 }
515 } else {
516 fprintf(out, "self.%s, ", get_variable_name(atomField.name).c_str());
517 }
518 }
519 fprintf(out, ")\n");
520 fprintf(out, " }\n");
521 fprintf(out, " }\n\n");
522 }
523
write_rust_stats_write_atoms(FILE * out,const AtomDeclSet & atomDeclSet,const AtomDecl & attributionDecl,const AtomDeclSet & nonChainedAtomDeclSet,const int minApiLevel,const char * headerCrate)524 static int write_rust_stats_write_atoms(FILE* out, const AtomDeclSet& atomDeclSet,
525 const AtomDecl& attributionDecl,
526 const AtomDeclSet& nonChainedAtomDeclSet,
527 const int minApiLevel, const char* headerCrate) {
528 for (const auto& atomDecl : atomDeclSet) {
529 // TODO(b/216543320): support repeated fields in Rust
530 if (std::find_if(atomDecl->fields.begin(), atomDecl->fields.end(),
531 [](const AtomField& atomField) {
532 return is_repeated_field(atomField.javaType);
533 }) != atomDecl->fields.end()) {
534 continue;
535 }
536 fprintf(out, "pub mod %s {\n", atomDecl->name.c_str());
537 fprintf(out, " use statspull_bindgen::*;\n");
538 fprintf(out, " #[allow(unused)]\n");
539 fprintf(out, " use std::convert::TryInto;\n");
540 fprintf(out, "\n");
541 write_rust_atom_constant_values(out, atomDecl);
542 write_rust_struct(out, atomDecl, attributionDecl, headerCrate);
543 const int ret = write_rust_stats_write_method(out, atomDecl, attributionDecl, minApiLevel,
544 headerCrate);
545 if (ret != 0) {
546 return ret;
547 }
548 auto nonChained = nonChainedAtomDeclSet.find(atomDecl);
549 if (nonChained != nonChainedAtomDeclSet.end()) {
550 write_rust_stats_write_non_chained_method(out, *nonChained, attributionDecl,
551 headerCrate);
552 }
553 fprintf(out, "}\n");
554 }
555 return 0;
556 }
557
write_stats_log_rust_header(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const char * headerCrate)558 void write_stats_log_rust_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
559 const char* headerCrate) {
560 // Print prelude
561 fprintf(out, "// This file is autogenerated.\n");
562 fprintf(out, "\n");
563 fprintf(out, "#[derive(thiserror::Error, Debug)]\n");
564 fprintf(out, "pub enum StatsError {\n");
565 fprintf(out, " #[error(\"Return error {0:?}\")]\n");
566 fprintf(out, " Return(i32),\n");
567 fprintf(out, " #[error(transparent)]\n");
568 fprintf(out, " NullChar(#[from] std::ffi::NulError),\n");
569 fprintf(out, " #[error(transparent)]\n");
570 fprintf(out, " Conversion(#[from] std::num::TryFromIntError),\n");
571 fprintf(out, "}\n");
572 fprintf(out, "\n");
573 fprintf(out, "pub type StatsResult = std::result::Result<(), StatsError>;\n");
574 fprintf(out, "\n");
575 fprintf(out, "pub trait Stat {\n");
576 fprintf(out,
577 " fn add_astats_event(&self,"
578 " pulled_data: &mut statspull_bindgen::AStatsEventList)"
579 " -> StatsResult;\n");
580 fprintf(out, "}\n");
581 fprintf(out, "\n");
582
583 write_rust_atom_constants(out, atoms, attributionDecl, headerCrate);
584 }
585
write_stats_log_rust(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const int minApiLevel,const char * headerCrate)586 int write_stats_log_rust(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
587 const int minApiLevel, const char* headerCrate) {
588 // Print prelude
589 fprintf(out, "// This file is autogenerated.\n");
590 fprintf(out, "\n");
591 fprintf(out, "struct AStatsEventDropper(*mut statspull_bindgen::AStatsEvent);\n");
592 fprintf(out, "\n");
593 fprintf(out, "impl Drop for AStatsEventDropper {\n");
594 fprintf(out, " fn drop(&mut self) {\n");
595 fprintf(out, " unsafe { statspull_bindgen::AStatsEvent_release(self.0) }\n");
596 fprintf(out, " }\n");
597 fprintf(out, "}\n");
598 fprintf(out, "\n");
599
600 write_rust_annotation_constants(out);
601
602 const int errorCount = write_rust_stats_write_atoms(
603 out, atoms.decls, attributionDecl, atoms.non_chained_decls, minApiLevel, headerCrate);
604
605 return errorCount;
606 }
607
608 } // namespace stats_log_api_gen
609 } // namespace android
610