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