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