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