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,
103 const AtomDecl& atomDecl,
104 const AtomDecl& attributionDecl,
105 bool isCode,
106 bool isNonChained,
107 const char* headerCrate) {
108 // To make the generated code pretty, add newlines between arguments.
109 const char* separator = (isCode ? "\n" : " ");
110 if (isCode) {
111 fprintf(out, " pub fn %s(", namePrefix);
112 } else {
113 fprintf(out, " %s::%s(", atomDecl.name.c_str(), namePrefix);
114 }
115 if (isCode) {
116 fprintf(out, "\n");
117 }
118 if (atomDecl.atomType == ATOM_TYPE_PULLED) {
119 if (isCode) {
120 fprintf(out, " ");
121 }
122 fprintf(out, "pulled_data_: &mut AStatsEventList,%s", separator);
123 }
124 for (int i = 0; i < atomDecl.fields.size(); i++) {
125 const AtomField &atomField = atomDecl.fields[i];
126 const java_type_t& type = atomField.javaType;
127 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
128 for (int j = 0; j < attributionDecl.fields.size(); j++) {
129 const AtomField& chainField = attributionDecl.fields[j];
130 if (isCode) {
131 fprintf(out, " ");
132 }
133 fprintf(out, "%s_chain_: &[%s],%s", chainField.name.c_str(),
134 rust_type_name(chainField.javaType, false), separator);
135 }
136 } else {
137 if (isCode) {
138 fprintf(out, " ");
139 }
140 // Other arguments can have the same name as these non-chained ones,
141 // so append something.
142 if (isNonChained && i < 2) {
143 fprintf(out, "%s_non_chained_", atomField.name.c_str());
144 } else {
145 fprintf(out, "%s", get_variable_name(atomField.name).c_str());
146 }
147 if (type == JAVA_TYPE_ENUM) {
148 fprintf(out, ": %s,%s", make_camel_case_name(atomField.name).c_str(),
149 separator);
150 } else {
151 fprintf(out, ": %s,%s", rust_type_name(type, false), separator);
152 }
153 }
154 }
155 fprintf(out, " ) -> %s::StatsResult", headerCrate);
156 if (isCode) {
157 fprintf(out, " {");
158 }
159 fprintf(out, "\n");
160 }
161
write_rust_usage(FILE * out,const string & method_name,const shared_ptr<AtomDecl> atom,const AtomDecl & attributionDecl,bool isNonChained,const char * headerCrate)162 static bool write_rust_usage(FILE* out, const string& method_name, const shared_ptr<AtomDecl> atom,
163 const AtomDecl& attributionDecl, bool isNonChained,
164 const char* headerCrate) {
165 fprintf(out, " // Definition: ");
166 write_rust_method_signature(out, method_name.c_str(), *atom, attributionDecl,
167 false, isNonChained, headerCrate);
168 return true;
169 }
170
write_rust_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const char * headerCrate)171 static void write_rust_atom_constants(FILE* out, const Atoms& atoms,
172 const AtomDecl& attributionDecl,
173 const char* headerCrate) {
174 fprintf(out, "// Constants for atom codes.\n");
175 fprintf(out, "#[derive(Clone, Copy)]\n");
176 fprintf(out, "pub enum Atoms {\n");
177
178 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
179 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
180
181 for (const shared_ptr<AtomDecl>& atomDecl : atoms.decls) {
182 string constant = make_camel_case_name(atomDecl->name);
183 fprintf(out, "\n");
184 fprintf(out, " // %s %s\n", atomDecl->message.c_str(), atomDecl->name.c_str());
185 bool isSupported = write_rust_usage(out, "// stats_write", atomDecl,
186 attributionDecl, false, headerCrate);
187 if (!isSupported) {
188 continue;
189 }
190
191 auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atomDecl->code);
192 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
193 write_rust_usage(out, "stats_write_non_chained", *non_chained_decl->second,
194 attributionDecl, true, headerCrate);
195 }
196 fprintf(out, " %s = %d,\n", constant.c_str(), atomDecl->code);
197 }
198
199 fprintf(out, "\n");
200 fprintf(out, "}\n");
201 fprintf(out, "\n");
202 }
203
write_rust_atom_constant_values(FILE * out,const shared_ptr<AtomDecl> & atomDecl)204 static void write_rust_atom_constant_values(FILE* out, const shared_ptr<AtomDecl>& atomDecl) {
205 bool hasConstants = false;
206 for (const AtomField& field : atomDecl->fields) {
207 if (field.javaType == JAVA_TYPE_ENUM) {
208 fprintf(out, " #[repr(i32)]\n");
209 fprintf(out, " #[derive(Clone, Copy, Eq, PartialEq)]\n");
210 fprintf(out, " pub enum %s {\n", make_camel_case_name(field.name).c_str());
211 for (map<int, string>::const_iterator value = field.enumValues.begin();
212 value != field.enumValues.end(); value++) {
213 fprintf(out, " %s = %d,\n",
214 make_camel_case_name(value->second).c_str(), value->first);
215 }
216 fprintf(out, " }\n");
217 hasConstants = true;
218 }
219 }
220 if (hasConstants) {
221 fprintf(out, "\n");
222 }
223 }
224
write_rust_annotation_constants(FILE * out)225 static void write_rust_annotation_constants(FILE* out) {
226 fprintf(out, "// Annotation constants.\n");
227 // The annotation names are all prefixed with AnnotationId.
228 // Ideally that would go into the enum name instead,
229 // but I don't want to modify the actual name strings in case they change in the future.
230 fprintf(out, "#[allow(clippy::enum_variant_names)]\n");
231 fprintf(out, "#[repr(u8)]\n");
232 fprintf(out, "enum Annotations {\n");
233
234 const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
235 for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
236 fprintf(out, " %s = %hhu,\n", make_camel_case_name(name).c_str(), id);
237 }
238 fprintf(out, "}\n\n");
239 }
240
241 // This is mostly copied from the version in native_writer with some minor changes.
242 // Note that argIndex is 1 for the first argument.
write_annotations(FILE * out,int argIndex,const AtomDecl & atomDecl,const string & methodPrefix,const string & methodSuffix)243 static void write_annotations(FILE* out, int argIndex, const AtomDecl& atomDecl,
244 const string& methodPrefix, const string& methodSuffix) {
245 const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
246 auto annotationsIt = atomDecl.fieldNumberToAnnotations.find(argIndex);
247 if (annotationsIt == atomDecl.fieldNumberToAnnotations.end()) {
248 return;
249 }
250 int resetState = -1;
251 int defaultState = -1;
252 for (const shared_ptr<Annotation>& annotation : annotationsIt->second) {
253 const string& annotationConstant = ANNOTATION_ID_CONSTANTS.at(annotation->annotationId);
254 switch (annotation->type) {
255 case ANNOTATION_TYPE_INT:
256 if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
257 resetState = annotation->value.intValue;
258 } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
259 defaultState = annotation->value.intValue;
260 } else {
261 fprintf(out, " %saddInt32Annotation(%scrate::Annotations::%s as u8, %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, " %saddBoolAnnotation(%scrate::Annotations::%s as u8, %s);\n",
269 methodPrefix.c_str(), methodSuffix.c_str(),
270 make_camel_case_name(annotationConstant).c_str(),
271 annotation->value.boolValue ? "true" : "false");
272 break;
273 default:
274 break;
275 }
276 }
277 if (defaultState != -1 && resetState != -1) {
278 const string& annotationConstant =
279 ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET);
280 const AtomField& field = atomDecl.fields[argIndex - 1];
281 if (field.javaType == JAVA_TYPE_ENUM) {
282 fprintf(out, " if %s as i32 == %d {\n",
283 get_variable_name(field.name).c_str(), resetState);
284 } else {
285 fprintf(out, " if %s == %d {\n",
286 get_variable_name(field.name).c_str(), resetState);
287 }
288 fprintf(out, " %saddInt32Annotation(%scrate::Annotations::%s as u8, %d);\n",
289 methodPrefix.c_str(), methodSuffix.c_str(),
290 make_camel_case_name(annotationConstant).c_str(),
291 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,
298 const int minApiLevel,
299 const char* headerCrate) {
300 fprintf(out, " unsafe {\n");
301 if (minApiLevel == API_Q) {
302 fprintf(stderr, "TODO: Do we need to handle this case?");
303 return 1;
304 }
305 if (atomDecl.atomType == ATOM_TYPE_PUSHED) {
306 fprintf(out, " let __event = AStatsEvent_obtain();\n");
307 fprintf(out, " let __dropper = crate::AStatsEventDropper(__event);\n");
308 } else {
309 fprintf(out, " let __event = AStatsEventList_addStatsEvent(pulled_data_);\n");
310 }
311 fprintf(out, " AStatsEvent_setAtomId(__event, %s::Atoms::%s as u32);\n",
312 headerCrate,
313 make_camel_case_name(atomDecl.name).c_str());
314 write_annotations(out, ATOM_ID_FIELD_NUMBER, atomDecl, "AStatsEvent_", "__event, ");
315 for (int i = 0; i < atomDecl.fields.size(); i++) {
316 const AtomField& atomField = atomDecl.fields[i];
317 const string& name = get_variable_name(atomField.name);
318 const java_type_t& type = atomField.javaType;
319 switch (type) {
320 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
321 const char* uidName = attributionDecl.fields.front().name.c_str();
322 const char* tagName = attributionDecl.fields.back().name.c_str();
323 fprintf(out,
324 " let uids = %s_chain_.iter().map(|n| (*n).try_into())"
325 ".collect::<std::result::Result<Vec<_>, _>>()?;\n"
326 " let str_arr = %s_chain_.iter().map(|s| std::ffi::CString::new(*s))"
327 ".collect::<std::result::Result<Vec<_>, _>>()?;\n"
328 " let ptr_arr = str_arr.iter().map(|s| s.as_ptr())"
329 ".collect::<std::vec::Vec<_>>();\n"
330 " AStatsEvent_writeAttributionChain(__event, uids.as_ptr(),\n"
331 " ptr_arr.as_ptr(), %s_chain_.len().try_into()?);\n",
332 uidName, tagName, uidName);
333 break;
334 }
335 case JAVA_TYPE_BYTE_ARRAY:
336 fprintf(out,
337 " AStatsEvent_writeByteArray(__event, "
338 "%s.as_ptr(), %s.len());\n",
339 name.c_str(), name.c_str());
340 break;
341 case JAVA_TYPE_BOOLEAN:
342 fprintf(out, " AStatsEvent_writeBool(__event, %s);\n", name.c_str());
343 break;
344 case JAVA_TYPE_ENUM:
345 fprintf(out, " AStatsEvent_writeInt32(__event, %s as i32);\n", 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, " if __ret >= 0 { %s::StatsResult::Ok(()) }"
371 " else { Err(%s::StatsError::Return(__ret)) }\n", headerCrate,
372 headerCrate);
373 } else {
374 fprintf(out, " AStatsEvent_build(__event);\n");
375 fprintf(out, " %s::StatsResult::Ok(())\n", headerCrate);
376 }
377 fprintf(out, " }\n");
378 return 0;
379 }
380
write_rust_stats_write_method(FILE * out,const shared_ptr<AtomDecl> & atomDecl,const AtomDecl & attributionDecl,const int minApiLevel,const char * headerCrate)381 static int write_rust_stats_write_method(FILE* out, const shared_ptr<AtomDecl>& atomDecl,
382 const AtomDecl& attributionDecl,
383 const int minApiLevel,
384 const char* headerCrate) {
385 if (atomDecl->atomType == ATOM_TYPE_PUSHED) {
386 write_rust_method_signature(out, "stats_write", *atomDecl, attributionDecl,
387 true, false, headerCrate);
388 } else {
389 write_rust_method_signature(out, "add_astats_event", *atomDecl, attributionDecl,
390 true, 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,
405 true, 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
436 || type == JAVA_TYPE_STRING || 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, " pub fn stats_write(&self)"
490 " -> %s::StatsResult {\n", headerCrate);
491 fprintf(out, " stats_write(");
492 } else {
493 fprintf(out, " fn add_astats_event(&self, pulled_data: &mut AStatsEventList)"
494 " -> %s::StatsResult {\n", headerCrate);
495 fprintf(out, " add_astats_event(pulled_data, ");
496 }
497 for (const AtomField& atomField : atomDecl->fields) {
498 const java_type_t& type = atomField.javaType;
499 if (type == JAVA_TYPE_ATTRIBUTION_CHAIN) {
500 for (const AtomField& chainField : attributionDecl.fields) {
501 fprintf(out, "self.%s_chain_, ", chainField.name.c_str());
502 }
503 } else {
504 fprintf(out, "self.%s, ", get_variable_name(atomField.name).c_str());
505 }
506 }
507 fprintf(out, ")\n");
508 fprintf(out, " }\n");
509 fprintf(out, " }\n\n");
510 }
511
write_rust_stats_write_atoms(FILE * out,const AtomDeclSet & atomDeclSet,const AtomDecl & attributionDecl,const AtomDeclSet & nonChainedAtomDeclSet,const int minApiLevel,const char * headerCrate)512 static int write_rust_stats_write_atoms(FILE* out, const AtomDeclSet& atomDeclSet,
513 const AtomDecl& attributionDecl,
514 const AtomDeclSet& nonChainedAtomDeclSet,
515 const int minApiLevel,
516 const char* headerCrate) {
517 for (const auto& atomDecl : atomDeclSet) {
518 // TODO(b/216543320): support repeated fields in Rust
519 if (std::find_if(atomDecl->fields.begin(), atomDecl->fields.end(),
520 [](const AtomField& atomField) {
521 return is_repeated_field(atomField.javaType);
522 }) != atomDecl->fields.end()) {
523 continue;
524 }
525 fprintf(out, "pub mod %s {\n", atomDecl->name.c_str());
526 fprintf(out, " use statspull_bindgen::*;\n");
527 fprintf(out, " #[allow(unused)]\n");
528 fprintf(out, " use std::convert::TryInto;\n");
529 fprintf(out, "\n");
530 write_rust_atom_constant_values(out, atomDecl);
531 write_rust_struct(out, atomDecl, attributionDecl, headerCrate);
532 int ret = write_rust_stats_write_method(out, atomDecl, attributionDecl,
533 minApiLevel, headerCrate);
534 if (ret != 0) {
535 return ret;
536 }
537 auto nonChained = nonChainedAtomDeclSet.find(atomDecl);
538 if (nonChained != nonChainedAtomDeclSet.end()) {
539 write_rust_stats_write_non_chained_method(out, *nonChained,
540 attributionDecl, headerCrate);
541 }
542 fprintf(out, "}\n");
543 }
544 return 0;
545 }
546
write_stats_log_rust_header(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const char * headerCrate)547 void write_stats_log_rust_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
548 const char* headerCrate) {
549 // Print prelude
550 fprintf(out, "// This file is autogenerated.\n");
551 fprintf(out, "\n");
552 fprintf(out, "#[derive(thiserror::Error, Debug)]\n");
553 fprintf(out, "pub enum StatsError {\n");
554 fprintf(out, " #[error(\"Return error {0:?}\")]\n");
555 fprintf(out, " Return(i32),\n");
556 fprintf(out, " #[error(transparent)]\n");
557 fprintf(out, " NullChar(#[from] std::ffi::NulError),\n");
558 fprintf(out, " #[error(transparent)]\n");
559 fprintf(out, " Conversion(#[from] std::num::TryFromIntError),\n");
560 fprintf(out, "}\n");
561 fprintf(out, "\n");
562 fprintf(out, "pub type StatsResult = std::result::Result<(), StatsError>;\n");
563 fprintf(out, "\n");
564 fprintf(out, "pub trait Stat {\n");
565 fprintf(out, " fn add_astats_event(&self,"
566 " pulled_data: &mut statspull_bindgen::AStatsEventList)"
567 " -> StatsResult;\n");
568 fprintf(out, "}\n");
569 fprintf(out, "\n");
570
571 write_rust_atom_constants(out, atoms, attributionDecl, headerCrate);
572 }
573
write_stats_log_rust(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const int minApiLevel,const char * headerCrate)574 int write_stats_log_rust(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
575 const int minApiLevel, const char* headerCrate) {
576 //Print prelude
577 fprintf(out, "// This file is autogenerated.\n");
578 fprintf(out, "\n");
579 fprintf(out, "struct AStatsEventDropper(*mut statspull_bindgen::AStatsEvent);\n");
580 fprintf(out, "\n");
581 fprintf(out, "impl Drop for AStatsEventDropper {\n");
582 fprintf(out, " fn drop(&mut self) {\n");
583 fprintf(out, " unsafe { statspull_bindgen::AStatsEvent_release(self.0) }\n");
584 fprintf(out, " }\n");
585 fprintf(out, "}\n");
586 fprintf(out, "\n");
587
588 write_rust_annotation_constants(out);
589
590 int errorCount = write_rust_stats_write_atoms(out, atoms.decls, attributionDecl,
591 atoms.non_chained_decls,
592 minApiLevel, headerCrate);
593
594 return errorCount;
595 }
596
597 } // namespace stats_log_api_gen
598 } // namespace android
599