• 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 <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