1 | /* |
2 | * Copyright 2018 Google Inc. All rights reserved. |
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 | // independent from idl_parser, since this code is not needed for most clients |
18 | |
19 | #include "flatbuffers/code_generators.h" |
20 | #include "flatbuffers/flatbuffers.h" |
21 | #include "flatbuffers/idl.h" |
22 | #include "flatbuffers/util.h" |
23 | |
24 | namespace flatbuffers { |
25 | |
26 | static std::string GeneratedFileName(const std::string &path, |
27 | const std::string &file_name) { |
28 | return path + file_name + "_generated.rs" ; |
29 | } |
30 | |
31 | // Convert a camelCaseIdentifier or CamelCaseIdentifier to a |
32 | // snake_case_indentifier. |
33 | std::string MakeSnakeCase(const std::string &in) { |
34 | std::string s; |
35 | for (size_t i = 0; i < in.length(); i++) { |
36 | if (i == 0) { |
37 | s += static_cast<char>(tolower(in[0])); |
38 | } else if (in[i] == '_') { |
39 | s += '_'; |
40 | } else if (!islower(in[i])) { |
41 | // Prevent duplicate underscores for Upper_Snake_Case strings |
42 | // and UPPERCASE strings. |
43 | if (islower(in[i - 1])) { |
44 | s += '_'; |
45 | } |
46 | s += static_cast<char>(tolower(in[i])); |
47 | } else { |
48 | s += in[i]; |
49 | } |
50 | } |
51 | return s; |
52 | } |
53 | |
54 | // Convert a string to all uppercase. |
55 | std::string MakeUpper(const std::string &in) { |
56 | std::string s; |
57 | for (size_t i = 0; i < in.length(); i++) { |
58 | s += static_cast<char>(toupper(in[i])); |
59 | } |
60 | return s; |
61 | } |
62 | |
63 | // Encapsulate all logical field types in this enum. This allows us to write |
64 | // field logic based on type switches, instead of branches on the properties |
65 | // set on the Type. |
66 | // TODO(rw): for backwards compatibility, we can't use a strict `enum class` |
67 | // declaration here. could we use the `-Wswitch-enum` warning to |
68 | // achieve the same effect? |
69 | enum FullType { |
70 | ftInteger = 0, |
71 | ftFloat = 1, |
72 | ftBool = 2, |
73 | |
74 | ftStruct = 3, |
75 | ftTable = 4, |
76 | |
77 | ftEnumKey = 5, |
78 | ftUnionKey = 6, |
79 | |
80 | ftUnionValue = 7, |
81 | |
82 | // TODO(rw): bytestring? |
83 | ftString = 8, |
84 | |
85 | ftVectorOfInteger = 9, |
86 | ftVectorOfFloat = 10, |
87 | ftVectorOfBool = 11, |
88 | ftVectorOfEnumKey = 12, |
89 | ftVectorOfStruct = 13, |
90 | ftVectorOfTable = 14, |
91 | ftVectorOfString = 15, |
92 | ftVectorOfUnionValue = 16, |
93 | }; |
94 | |
95 | // Convert a Type to a FullType (exhaustive). |
96 | FullType GetFullType(const Type &type) { |
97 | // N.B. The order of these conditionals matters for some types. |
98 | |
99 | if (type.base_type == BASE_TYPE_STRING) { |
100 | return ftString; |
101 | } else if (type.base_type == BASE_TYPE_STRUCT) { |
102 | if (type.struct_def->fixed) { |
103 | return ftStruct; |
104 | } else { |
105 | return ftTable; |
106 | } |
107 | } else if (type.base_type == BASE_TYPE_VECTOR) { |
108 | switch (GetFullType(type.VectorType())) { |
109 | case ftInteger: { |
110 | return ftVectorOfInteger; |
111 | } |
112 | case ftFloat: { |
113 | return ftVectorOfFloat; |
114 | } |
115 | case ftBool: { |
116 | return ftVectorOfBool; |
117 | } |
118 | case ftStruct: { |
119 | return ftVectorOfStruct; |
120 | } |
121 | case ftTable: { |
122 | return ftVectorOfTable; |
123 | } |
124 | case ftString: { |
125 | return ftVectorOfString; |
126 | } |
127 | case ftEnumKey: { |
128 | return ftVectorOfEnumKey; |
129 | } |
130 | case ftUnionKey: |
131 | case ftUnionValue: { |
132 | FLATBUFFERS_ASSERT(false && "vectors of unions are unsupported" ); |
133 | break; |
134 | } |
135 | default: { |
136 | FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported" ); |
137 | } |
138 | } |
139 | } else if (type.enum_def != nullptr) { |
140 | if (type.enum_def->is_union) { |
141 | if (type.base_type == BASE_TYPE_UNION) { |
142 | return ftUnionValue; |
143 | } else if (IsInteger(type.base_type)) { |
144 | return ftUnionKey; |
145 | } else { |
146 | FLATBUFFERS_ASSERT(false && "unknown union field type" ); |
147 | } |
148 | } else { |
149 | return ftEnumKey; |
150 | } |
151 | } else if (IsScalar(type.base_type)) { |
152 | if (IsBool(type.base_type)) { |
153 | return ftBool; |
154 | } else if (IsInteger(type.base_type)) { |
155 | return ftInteger; |
156 | } else if (IsFloat(type.base_type)) { |
157 | return ftFloat; |
158 | } else { |
159 | FLATBUFFERS_ASSERT(false && "unknown number type" ); |
160 | } |
161 | } |
162 | |
163 | FLATBUFFERS_ASSERT(false && "completely unknown type" ); |
164 | |
165 | // this is only to satisfy the compiler's return analysis. |
166 | return ftBool; |
167 | } |
168 | |
169 | // If the second parameter is false then wrap the first with Option<...> |
170 | std::string WrapInOptionIfNotRequired(std::string s, bool required) { |
171 | if (required) { |
172 | return s; |
173 | } else { |
174 | return "Option<" + s + ">" ; |
175 | } |
176 | } |
177 | |
178 | // If the second parameter is false then add .unwrap() |
179 | std::string AddUnwrapIfRequired(std::string s, bool required) { |
180 | if (required) { |
181 | return s + ".unwrap()" ; |
182 | } else { |
183 | return s; |
184 | } |
185 | } |
186 | |
187 | namespace rust { |
188 | |
189 | class RustGenerator : public BaseGenerator { |
190 | public: |
191 | RustGenerator(const Parser &parser, const std::string &path, |
192 | const std::string &file_name) |
193 | : BaseGenerator(parser, path, file_name, "" , "::" ), |
194 | cur_name_space_(nullptr) { |
195 | const char *keywords[] = { |
196 | // list taken from: |
197 | // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html |
198 | // |
199 | // we write keywords one per line so that we can easily compare them with |
200 | // changes to that webpage in the future. |
201 | |
202 | // currently-used keywords |
203 | "as" , |
204 | "break" , |
205 | "const" , |
206 | "continue" , |
207 | "crate" , |
208 | "else" , |
209 | "enum" , |
210 | "extern" , |
211 | "false" , |
212 | "fn" , |
213 | "for" , |
214 | "if" , |
215 | "impl" , |
216 | "in" , |
217 | "let" , |
218 | "loop" , |
219 | "match" , |
220 | "mod" , |
221 | "move" , |
222 | "mut" , |
223 | "pub" , |
224 | "ref" , |
225 | "return" , |
226 | "Self" , |
227 | "self" , |
228 | "static" , |
229 | "struct" , |
230 | "super" , |
231 | "trait" , |
232 | "true" , |
233 | "type" , |
234 | "unsafe" , |
235 | "use" , |
236 | "where" , |
237 | "while" , |
238 | |
239 | // future possible keywords |
240 | "abstract" , |
241 | "alignof" , |
242 | "become" , |
243 | "box" , |
244 | "do" , |
245 | "final" , |
246 | "macro" , |
247 | "offsetof" , |
248 | "override" , |
249 | "priv" , |
250 | "proc" , |
251 | "pure" , |
252 | "sizeof" , |
253 | "typeof" , |
254 | "unsized" , |
255 | "virtual" , |
256 | "yield" , |
257 | |
258 | // other rust terms we should not use |
259 | "std" , |
260 | "usize" , |
261 | "isize" , |
262 | "u8" , |
263 | "i8" , |
264 | "u16" , |
265 | "i16" , |
266 | "u32" , |
267 | "i32" , |
268 | "u64" , |
269 | "i64" , |
270 | "u128" , |
271 | "i128" , |
272 | "f32" , |
273 | "f64" , |
274 | |
275 | // These are terms the code generator can implement on types. |
276 | // |
277 | // In Rust, the trait resolution rules (as described at |
278 | // https://github.com/rust-lang/rust/issues/26007) mean that, as long |
279 | // as we impl table accessors as inherent methods, we'll never create |
280 | // conflicts with these keywords. However, that's a fairly nuanced |
281 | // implementation detail, and how we implement methods could change in |
282 | // the future. as a result, we proactively block these out as reserved |
283 | // words. |
284 | "follow" , |
285 | "push" , |
286 | "size" , |
287 | "alignment" , |
288 | "to_little_endian" , |
289 | "from_little_endian" , |
290 | nullptr }; |
291 | for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw); |
292 | } |
293 | |
294 | // Iterate through all definitions we haven't generated code for (enums, |
295 | // structs, and tables) and output them to a single file. |
296 | bool generate() { |
297 | code_.Clear(); |
298 | code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n" ; |
299 | |
300 | assert(!cur_name_space_); |
301 | |
302 | // Generate imports for the global scope in case no namespace is used |
303 | // in the schema file. |
304 | GenNamespaceImports(0); |
305 | code_ += "" ; |
306 | |
307 | // Generate all code in their namespaces, once, because Rust does not |
308 | // permit re-opening modules. |
309 | // |
310 | // TODO(rw): Use a set data structure to reduce namespace evaluations from |
311 | // O(n**2) to O(n). |
312 | for (auto ns_it = parser_.namespaces_.begin(); |
313 | ns_it != parser_.namespaces_.end(); |
314 | ++ns_it) { |
315 | const auto &ns = *ns_it; |
316 | |
317 | // Generate code for all the enum declarations. |
318 | for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); |
319 | ++it) { |
320 | const auto &enum_def = **it; |
321 | if (enum_def.defined_namespace != ns) { continue; } |
322 | if (!enum_def.generated) { |
323 | SetNameSpace(enum_def.defined_namespace); |
324 | GenEnum(enum_def); |
325 | } |
326 | } |
327 | |
328 | // Generate code for all structs. |
329 | for (auto it = parser_.structs_.vec.begin(); |
330 | it != parser_.structs_.vec.end(); ++it) { |
331 | const auto &struct_def = **it; |
332 | if (struct_def.defined_namespace != ns) { continue; } |
333 | if (struct_def.fixed && !struct_def.generated) { |
334 | SetNameSpace(struct_def.defined_namespace); |
335 | GenStruct(struct_def); |
336 | } |
337 | } |
338 | |
339 | // Generate code for all tables. |
340 | for (auto it = parser_.structs_.vec.begin(); |
341 | it != parser_.structs_.vec.end(); ++it) { |
342 | const auto &struct_def = **it; |
343 | if (struct_def.defined_namespace != ns) { continue; } |
344 | if (!struct_def.fixed && !struct_def.generated) { |
345 | SetNameSpace(struct_def.defined_namespace); |
346 | GenTable(struct_def); |
347 | } |
348 | } |
349 | |
350 | // Generate global helper functions. |
351 | if (parser_.root_struct_def_) { |
352 | auto &struct_def = *parser_.root_struct_def_; |
353 | if (struct_def.defined_namespace != ns) { continue; } |
354 | SetNameSpace(struct_def.defined_namespace); |
355 | GenRootTableFuncs(struct_def); |
356 | } |
357 | } |
358 | if (cur_name_space_) SetNameSpace(nullptr); |
359 | |
360 | const auto file_path = GeneratedFileName(path_, file_name_); |
361 | const auto final_code = code_.ToString(); |
362 | return SaveFile(file_path.c_str(), final_code, false); |
363 | } |
364 | |
365 | private: |
366 | CodeWriter code_; |
367 | |
368 | std::set<std::string> keywords_; |
369 | |
370 | // This tracks the current namespace so we can insert namespace declarations. |
371 | const Namespace *cur_name_space_; |
372 | |
373 | const Namespace *CurrentNameSpace() const { return cur_name_space_; } |
374 | |
375 | // Determine if a Type needs a lifetime template parameter when used in the |
376 | // Rust builder args. |
377 | bool TableBuilderTypeNeedsLifetime(const Type &type) const { |
378 | switch (GetFullType(type)) { |
379 | case ftInteger: |
380 | case ftFloat: |
381 | case ftBool: |
382 | case ftEnumKey: |
383 | case ftUnionKey: |
384 | case ftUnionValue: { return false; } |
385 | default: { return true; } |
386 | } |
387 | } |
388 | |
389 | // Determine if a table args rust type needs a lifetime template parameter. |
390 | bool TableBuilderArgsNeedsLifetime(const StructDef &struct_def) const { |
391 | FLATBUFFERS_ASSERT(!struct_def.fixed); |
392 | |
393 | for (auto it = struct_def.fields.vec.begin(); |
394 | it != struct_def.fields.vec.end(); ++it) { |
395 | const auto &field = **it; |
396 | if (field.deprecated) { |
397 | continue; |
398 | } |
399 | |
400 | if (TableBuilderTypeNeedsLifetime(field.value.type)) { |
401 | return true; |
402 | } |
403 | } |
404 | |
405 | return false; |
406 | } |
407 | |
408 | // Determine if a Type needs to be copied (for endian safety) when used in a |
409 | // Struct. |
410 | bool StructMemberAccessNeedsCopy(const Type &type) const { |
411 | switch (GetFullType(type)) { |
412 | case ftInteger: // requires endian swap |
413 | case ftFloat: // requires endian swap |
414 | case ftBool: // no endian-swap, but do the copy for UX consistency |
415 | case ftEnumKey: { return true; } // requires endian swap |
416 | case ftStruct: { return false; } // no endian swap |
417 | default: { |
418 | // logic error: no other types can be struct members. |
419 | FLATBUFFERS_ASSERT(false && "invalid struct member type" ); |
420 | return false; // only to satisfy compiler's return analysis |
421 | } |
422 | } |
423 | } |
424 | |
425 | std::string EscapeKeyword(const std::string &name) const { |
426 | return keywords_.find(name) == keywords_.end() ? name : name + "_" ; |
427 | } |
428 | |
429 | std::string Name(const Definition &def) const { |
430 | return EscapeKeyword(def.name); |
431 | } |
432 | |
433 | std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); } |
434 | |
435 | std::string WrapInNameSpace(const Definition &def) const { |
436 | return WrapInNameSpace(def.defined_namespace, Name(def)); |
437 | } |
438 | std::string WrapInNameSpace(const Namespace *ns, |
439 | const std::string &name) const { |
440 | if (CurrentNameSpace() == ns) return name; |
441 | std::string prefix = GetRelativeNamespaceTraversal(CurrentNameSpace(), ns); |
442 | return prefix + name; |
443 | } |
444 | |
445 | // Determine the namespace traversal needed from the Rust crate root. |
446 | // This may be useful in the future for referring to included files, but is |
447 | // currently unused. |
448 | std::string GetAbsoluteNamespaceTraversal(const Namespace *dst) const { |
449 | std::stringstream stream; |
450 | |
451 | stream << "::" ; |
452 | for (auto d = dst->components.begin(); d != dst->components.end(); d++) { |
453 | stream << MakeSnakeCase(*d) + "::" ; |
454 | } |
455 | return stream.str(); |
456 | } |
457 | |
458 | // Determine the relative namespace traversal needed to reference one |
459 | // namespace from another namespace. This is useful because it does not force |
460 | // the user to have a particular file layout. (If we output absolute |
461 | // namespace paths, that may require users to organize their Rust crates in a |
462 | // particular way.) |
463 | std::string GetRelativeNamespaceTraversal(const Namespace *src, |
464 | const Namespace *dst) const { |
465 | // calculate the path needed to reference dst from src. |
466 | // example: f(A::B::C, A::B::C) -> (none) |
467 | // example: f(A::B::C, A::B) -> super:: |
468 | // example: f(A::B::C, A::B::D) -> super::D |
469 | // example: f(A::B::C, A) -> super::super:: |
470 | // example: f(A::B::C, D) -> super::super::super::D |
471 | // example: f(A::B::C, D::E) -> super::super::super::D::E |
472 | // example: f(A, D::E) -> super::D::E |
473 | // does not include leaf object (typically a struct type). |
474 | |
475 | size_t i = 0; |
476 | std::stringstream stream; |
477 | |
478 | auto s = src->components.begin(); |
479 | auto d = dst->components.begin(); |
480 | for(;;) { |
481 | if (s == src->components.end()) { break; } |
482 | if (d == dst->components.end()) { break; } |
483 | if (*s != *d) { break; } |
484 | s++; |
485 | d++; |
486 | i++; |
487 | } |
488 | |
489 | for (; s != src->components.end(); s++) { |
490 | stream << "super::" ; |
491 | } |
492 | for (; d != dst->components.end(); d++) { |
493 | stream << MakeSnakeCase(*d) + "::" ; |
494 | } |
495 | return stream.str(); |
496 | } |
497 | |
498 | // Generate a comment from the schema. |
499 | void (const std::vector<std::string> &dc, const char *prefix = "" ) { |
500 | std::string text; |
501 | ::flatbuffers::GenComment(dc, &text, nullptr, prefix); |
502 | code_ += text + "\\" ; |
503 | } |
504 | |
505 | // Return a Rust type from the table in idl.h. |
506 | std::string GetTypeBasic(const Type &type) const { |
507 | switch (GetFullType(type)) { |
508 | case ftInteger: |
509 | case ftFloat: |
510 | case ftBool: |
511 | case ftEnumKey: |
512 | case ftUnionKey: { break; } |
513 | default: { FLATBUFFERS_ASSERT(false && "incorrect type given" );} |
514 | } |
515 | |
516 | // clang-format off |
517 | static const char * const ctypename[] = { |
518 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \ |
519 | RTYPE) \ |
520 | #RTYPE, |
521 | FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) |
522 | #undef FLATBUFFERS_TD |
523 | // clang-format on |
524 | }; |
525 | |
526 | if (type.enum_def) { return WrapInNameSpace(*type.enum_def); } |
527 | return ctypename[type.base_type]; |
528 | } |
529 | |
530 | // Look up the native type for an enum. This will always be an integer like |
531 | // u8, i32, etc. |
532 | std::string GetEnumTypeForDecl(const Type &type) { |
533 | const auto ft = GetFullType(type); |
534 | if (!(ft == ftEnumKey || ft == ftUnionKey)) { |
535 | FLATBUFFERS_ASSERT(false && "precondition failed in GetEnumTypeForDecl" ); |
536 | } |
537 | |
538 | static const char *ctypename[] = { |
539 | // clang-format off |
540 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \ |
541 | RTYPE) \ |
542 | #RTYPE, |
543 | FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) |
544 | #undef FLATBUFFERS_TD |
545 | // clang-format on |
546 | }; |
547 | |
548 | // Enums can be bools, but their Rust representation must be a u8, as used |
549 | // in the repr attribute (#[repr(bool)] is an invalid attribute). |
550 | if (type.base_type == BASE_TYPE_BOOL) return "u8" ; |
551 | return ctypename[type.base_type]; |
552 | } |
553 | |
554 | // Return a Rust type for any type (scalar, table, struct) specifically for |
555 | // using a FlatBuffer. |
556 | std::string GetTypeGet(const Type &type) const { |
557 | switch (GetFullType(type)) { |
558 | case ftInteger: |
559 | case ftFloat: |
560 | case ftBool: |
561 | case ftEnumKey: |
562 | case ftUnionKey: { |
563 | return GetTypeBasic(type); } |
564 | case ftTable: { |
565 | return WrapInNameSpace(type.struct_def->defined_namespace, |
566 | type.struct_def->name) + "<'a>" ; } |
567 | default: { |
568 | return WrapInNameSpace(type.struct_def->defined_namespace, |
569 | type.struct_def->name); } |
570 | } |
571 | } |
572 | |
573 | std::string GetEnumValUse(const EnumDef &enum_def, |
574 | const EnumVal &enum_val) const { |
575 | return Name(enum_def) + "::" + Name(enum_val); |
576 | } |
577 | |
578 | // Generate an enum declaration, |
579 | // an enum string lookup table, |
580 | // an enum match function, |
581 | // and an enum array of values |
582 | void GenEnum(const EnumDef &enum_def) { |
583 | code_.SetValue("ENUM_NAME" , Name(enum_def)); |
584 | code_.SetValue("BASE_TYPE" , GetEnumTypeForDecl(enum_def.underlying_type)); |
585 | |
586 | GenComment(enum_def.doc_comment); |
587 | code_ += "#[allow(non_camel_case_types)]" ; |
588 | code_ += "#[repr({{BASE_TYPE}})]" ; |
589 | code_ += "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]" ; |
590 | code_ += "pub enum " + Name(enum_def) + " {" ; |
591 | |
592 | int64_t anyv = 0; |
593 | const EnumVal *minv = nullptr, *maxv = nullptr; |
594 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { |
595 | const auto &ev = **it; |
596 | |
597 | GenComment(ev.doc_comment, " " ); |
598 | code_.SetValue("KEY" , Name(ev)); |
599 | code_.SetValue("VALUE" , NumToString(ev.value)); |
600 | code_ += " {{KEY}} = {{VALUE}}," ; |
601 | |
602 | minv = !minv || minv->value > ev.value ? &ev : minv; |
603 | maxv = !maxv || maxv->value < ev.value ? &ev : maxv; |
604 | anyv |= ev.value; |
605 | } |
606 | |
607 | code_ += "" ; |
608 | code_ += "}" ; |
609 | code_ += "" ; |
610 | |
611 | code_.SetValue("ENUM_NAME" , Name(enum_def)); |
612 | code_.SetValue("ENUM_NAME_SNAKE" , MakeSnakeCase(Name(enum_def))); |
613 | code_.SetValue("ENUM_NAME_CAPS" , MakeUpper(MakeSnakeCase(Name(enum_def)))); |
614 | code_.SetValue("ENUM_MIN_BASE_VALUE" , NumToString(minv->value)); |
615 | code_.SetValue("ENUM_MAX_BASE_VALUE" , NumToString(maxv->value)); |
616 | |
617 | // Generate enum constants, and impls for Follow, EndianScalar, and Push. |
618 | code_ += "const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\" ; |
619 | code_ += "{{ENUM_MIN_BASE_VALUE}};" ; |
620 | code_ += "const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\" ; |
621 | code_ += "{{ENUM_MAX_BASE_VALUE}};" ; |
622 | code_ += "" ; |
623 | code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {" ; |
624 | code_ += " type Inner = Self;" ; |
625 | code_ += " #[inline]" ; |
626 | code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {" ; |
627 | code_ += " flatbuffers::read_scalar_at::<Self>(buf, loc)" ; |
628 | code_ += " }" ; |
629 | code_ += "}" ; |
630 | code_ += "" ; |
631 | code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {" ; |
632 | code_ += " #[inline]" ; |
633 | code_ += " fn to_little_endian(self) -> Self {" ; |
634 | code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});" ; |
635 | code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};" ; |
636 | code_ += " unsafe { *p }" ; |
637 | code_ += " }" ; |
638 | code_ += " #[inline]" ; |
639 | code_ += " fn from_little_endian(self) -> Self {" ; |
640 | code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});" ; |
641 | code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};" ; |
642 | code_ += " unsafe { *p }" ; |
643 | code_ += " }" ; |
644 | code_ += "}" ; |
645 | code_ += "" ; |
646 | code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {" ; |
647 | code_ += " type Output = {{ENUM_NAME}};" ; |
648 | code_ += " #[inline]" ; |
649 | code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {" ; |
650 | code_ += " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>" |
651 | "(dst, *self);" ; |
652 | code_ += " }" ; |
653 | code_ += "}" ; |
654 | code_ += "" ; |
655 | |
656 | // Generate an array of all enumeration values. |
657 | auto num_fields = NumToString(enum_def.size()); |
658 | code_ += "#[allow(non_camel_case_types)]" ; |
659 | code_ += "const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " + |
660 | num_fields + "] = [" ; |
661 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { |
662 | const auto &ev = **it; |
663 | auto value = GetEnumValUse(enum_def, ev); |
664 | auto suffix = *it != enum_def.Vals().back() ? "," : "" ; |
665 | code_ += " " + value + suffix; |
666 | } |
667 | code_ += "];" ; |
668 | code_ += "" ; |
669 | |
670 | // Generate a string table for enum values. |
671 | // Problem is, if values are very sparse that could generate really big |
672 | // tables. Ideally in that case we generate a map lookup instead, but for |
673 | // the moment we simply don't output a table at all. |
674 | auto range = |
675 | enum_def.vals.vec.back()->value - enum_def.vals.vec.front()->value + 1; |
676 | // Average distance between values above which we consider a table |
677 | // "too sparse". Change at will. |
678 | static const int kMaxSparseness = 5; |
679 | if (range / static_cast<int64_t>(enum_def.vals.vec.size()) < |
680 | kMaxSparseness) { |
681 | code_ += "#[allow(non_camel_case_types)]" ; |
682 | code_ += "const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " + |
683 | NumToString(range) + "] = [" ; |
684 | |
685 | auto val = enum_def.Vals().front()->value; |
686 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); |
687 | ++it) { |
688 | const auto &ev = **it; |
689 | while (val++ != ev.value) { code_ += " \"\"," ; } |
690 | auto suffix = *it != enum_def.vals.vec.back() ? "," : "" ; |
691 | code_ += " \"" + Name(ev) + "\"" + suffix; |
692 | } |
693 | code_ += "];" ; |
694 | code_ += "" ; |
695 | |
696 | code_ += "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> " |
697 | "&'static str {" ; |
698 | |
699 | code_ += " let index = e as {{BASE_TYPE}}\\" ; |
700 | if (enum_def.vals.vec.front()->value) { |
701 | auto vals = GetEnumValUse(enum_def, *enum_def.vals.vec.front()); |
702 | code_ += " - " + vals + " as {{BASE_TYPE}}\\" ; |
703 | } |
704 | code_ += ";" ; |
705 | |
706 | code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]" ; |
707 | code_ += "}" ; |
708 | code_ += "" ; |
709 | } |
710 | |
711 | if (enum_def.is_union) { |
712 | // Generate tyoesafe offset(s) for unions |
713 | code_.SetValue("NAME" , Name(enum_def)); |
714 | code_.SetValue("UNION_OFFSET_NAME" , Name(enum_def) + "UnionTableOffset" ); |
715 | code_ += "pub struct {{UNION_OFFSET_NAME}} {}" ; |
716 | } |
717 | } |
718 | |
719 | std::string GetFieldOffsetName(const FieldDef &field) { |
720 | return "VT_" + MakeUpper(Name(field)); |
721 | } |
722 | |
723 | std::string GetDefaultConstant(const FieldDef &field) { |
724 | return field.value.type.base_type == BASE_TYPE_FLOAT |
725 | ? field.value.constant + "" |
726 | : field.value.constant; |
727 | } |
728 | |
729 | std::string GetDefaultScalarValue(const FieldDef &field) { |
730 | switch (GetFullType(field.value.type)) { |
731 | case ftInteger: { return GetDefaultConstant(field); } |
732 | case ftFloat: { return GetDefaultConstant(field); } |
733 | case ftBool: { |
734 | return field.value.constant == "0" ? "false" : "true" ; |
735 | } |
736 | case ftUnionKey: |
737 | case ftEnumKey: { |
738 | auto ev = field.value.type.enum_def->ReverseLookup( |
739 | StringToInt(field.value.constant.c_str()), false); |
740 | assert(ev); |
741 | return WrapInNameSpace(field.value.type.enum_def->defined_namespace, |
742 | GetEnumValUse(*field.value.type.enum_def, *ev)); |
743 | } |
744 | |
745 | // All pointer-ish types have a default value of None, because they are |
746 | // wrapped in Option. |
747 | default: { return "None" ; } |
748 | } |
749 | } |
750 | |
751 | // Create the return type for fields in the *BuilderArgs structs that are |
752 | // used to create Tables. |
753 | // |
754 | // Note: we could make all inputs to the BuilderArgs be an Option, as well |
755 | // as all outputs. But, the UX of Flatbuffers is that the user doesn't get to |
756 | // know if the value is default or not, because there are three ways to |
757 | // return a default value: |
758 | // 1) return a stored value that happens to be the default, |
759 | // 2) return a hardcoded value because the relevant vtable field is not in |
760 | // the vtable, or |
761 | // 3) return a hardcoded value because the vtable field value is set to zero. |
762 | std::string TableBuilderArgsDefnType(const FieldDef &field, |
763 | const std::string lifetime) { |
764 | const Type& type = field.value.type; |
765 | |
766 | switch (GetFullType(type)) { |
767 | case ftInteger: |
768 | case ftFloat: |
769 | case ftBool: { |
770 | const auto typname = GetTypeBasic(type); |
771 | return typname; |
772 | } |
773 | case ftStruct: { |
774 | const auto typname = WrapInNameSpace(*type.struct_def); |
775 | return "Option<&" + lifetime + " " + typname + ">" ; |
776 | } |
777 | case ftTable: { |
778 | const auto typname = WrapInNameSpace(*type.struct_def); |
779 | return "Option<flatbuffers::WIPOffset<" + typname + "<" + lifetime + \ |
780 | ">>>" ; |
781 | } |
782 | case ftString: { |
783 | return "Option<flatbuffers::WIPOffset<&" + lifetime + " str>>" ; |
784 | } |
785 | case ftEnumKey: |
786 | case ftUnionKey: { |
787 | const auto typname = WrapInNameSpace(*type.enum_def); |
788 | return typname; |
789 | } |
790 | case ftUnionValue: { |
791 | const auto typname = WrapInNameSpace(*type.enum_def); |
792 | return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>" ; |
793 | } |
794 | |
795 | case ftVectorOfInteger: |
796 | case ftVectorOfFloat: { |
797 | const auto typname = GetTypeBasic(type.VectorType()); |
798 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
799 | lifetime + ", " + typname + ">>>" ; |
800 | } |
801 | case ftVectorOfBool: { |
802 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
803 | lifetime + ", bool>>>" ; |
804 | } |
805 | case ftVectorOfEnumKey: { |
806 | const auto typname = WrapInNameSpace(*type.enum_def); |
807 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
808 | lifetime + ", " + typname + ">>>" ; |
809 | } |
810 | case ftVectorOfStruct: { |
811 | const auto typname = WrapInNameSpace(*type.struct_def); |
812 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
813 | lifetime + ", " + typname + ">>>" ; |
814 | } |
815 | case ftVectorOfTable: { |
816 | const auto typname = WrapInNameSpace(*type.struct_def); |
817 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
818 | lifetime + ", flatbuffers::ForwardsUOffset<" + typname + \ |
819 | "<" + lifetime + ">>>>>" ; |
820 | } |
821 | case ftVectorOfString: { |
822 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
823 | lifetime + ", flatbuffers::ForwardsUOffset<&" + lifetime + \ |
824 | " str>>>>" ; |
825 | } |
826 | case ftVectorOfUnionValue: { |
827 | const auto typname = WrapInNameSpace(*type.enum_def) + \ |
828 | "UnionTableOffset" ; |
829 | return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \ |
830 | lifetime + ", flatbuffers::ForwardsUOffset<" |
831 | "flatbuffers::Table<" + lifetime + ">>>>" ; |
832 | } |
833 | } |
834 | return "INVALID_CODE_GENERATION" ; // for return analysis |
835 | } |
836 | |
837 | std::string TableBuilderArgsDefaultValue(const FieldDef &field) { |
838 | return GetDefaultScalarValue(field); |
839 | } |
840 | std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) { |
841 | switch (GetFullType(field.value.type)) { |
842 | case ftUnionKey: |
843 | case ftEnumKey: { |
844 | const std::string basetype = GetTypeBasic(field.value.type); |
845 | return GetDefaultScalarValue(field); |
846 | } |
847 | |
848 | default: { return GetDefaultScalarValue(field); } |
849 | } |
850 | } |
851 | |
852 | std::string TableBuilderArgsAddFuncType(const FieldDef &field, |
853 | const std::string lifetime) { |
854 | const Type& type = field.value.type; |
855 | |
856 | switch (GetFullType(field.value.type)) { |
857 | case ftVectorOfStruct: { |
858 | const auto typname = WrapInNameSpace(*type.struct_def); |
859 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
860 | ", " + typname + ">>" ; |
861 | } |
862 | case ftVectorOfTable: { |
863 | const auto typname = WrapInNameSpace(*type.struct_def); |
864 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
865 | ", flatbuffers::ForwardsUOffset<" + typname + \ |
866 | "<" + lifetime + ">>>>" ; |
867 | } |
868 | case ftVectorOfInteger: |
869 | case ftVectorOfFloat: { |
870 | const auto typname = GetTypeBasic(type.VectorType()); |
871 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
872 | ", " + typname + ">>" ; |
873 | } |
874 | case ftVectorOfBool: { |
875 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
876 | ", bool>>" ; |
877 | } |
878 | case ftVectorOfString: { |
879 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
880 | ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>" ; |
881 | } |
882 | case ftVectorOfEnumKey: { |
883 | const auto typname = WrapInNameSpace(*type.enum_def); |
884 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
885 | ", " + typname + ">>" ; |
886 | } |
887 | case ftVectorOfUnionValue: { |
888 | const auto typname = WrapInNameSpace(*type.enum_def); |
889 | return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \ |
890 | ", flatbuffers::ForwardsUOffset<flatbuffers::Table<" + \ |
891 | lifetime + ">>>" ; |
892 | } |
893 | case ftEnumKey: { |
894 | const auto typname = WrapInNameSpace(*type.enum_def); |
895 | return typname; |
896 | } |
897 | case ftStruct: { |
898 | const auto typname = WrapInNameSpace(*type.struct_def); |
899 | return "&" + lifetime + " " + typname + "" ; |
900 | } |
901 | case ftTable: { |
902 | const auto typname = WrapInNameSpace(*type.struct_def); |
903 | return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>" ; |
904 | } |
905 | case ftInteger: |
906 | case ftFloat: { |
907 | const auto typname = GetTypeBasic(type); |
908 | return typname; |
909 | } |
910 | case ftBool: { |
911 | return "bool" ; |
912 | } |
913 | case ftString: { |
914 | return "flatbuffers::WIPOffset<&" + lifetime + " str>" ; |
915 | } |
916 | case ftUnionKey: { |
917 | const auto typname = WrapInNameSpace(*type.enum_def); |
918 | return typname; |
919 | } |
920 | case ftUnionValue: { |
921 | const auto typname = WrapInNameSpace(*type.enum_def); |
922 | return "flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>" ; |
923 | } |
924 | } |
925 | |
926 | return "INVALID_CODE_GENERATION" ; // for return analysis |
927 | } |
928 | |
929 | std::string TableBuilderArgsAddFuncBody(const FieldDef &field) { |
930 | const Type& type = field.value.type; |
931 | |
932 | switch (GetFullType(field.value.type)) { |
933 | case ftInteger: |
934 | case ftFloat: { |
935 | const auto typname = GetTypeBasic(field.value.type); |
936 | return "self.fbb_.push_slot::<" + typname + ">" ; |
937 | } |
938 | case ftBool: { |
939 | return "self.fbb_.push_slot::<bool>" ; |
940 | } |
941 | |
942 | case ftEnumKey: |
943 | case ftUnionKey: { |
944 | const auto underlying_typname = GetTypeBasic(type); |
945 | return "self.fbb_.push_slot::<" + underlying_typname + ">" ; |
946 | } |
947 | |
948 | case ftStruct: { |
949 | const std::string typname = WrapInNameSpace(*type.struct_def); |
950 | return "self.fbb_.push_slot_always::<&" + typname + ">" ; |
951 | } |
952 | case ftTable: { |
953 | const auto typname = WrapInNameSpace(*type.struct_def); |
954 | return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<" + \ |
955 | typname + ">>" ; |
956 | } |
957 | |
958 | case ftUnionValue: |
959 | case ftString: |
960 | case ftVectorOfInteger: |
961 | case ftVectorOfFloat: |
962 | case ftVectorOfBool: |
963 | case ftVectorOfEnumKey: |
964 | case ftVectorOfStruct: |
965 | case ftVectorOfTable: |
966 | case ftVectorOfString: |
967 | case ftVectorOfUnionValue: { |
968 | return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>" ; |
969 | } |
970 | } |
971 | return "INVALID_CODE_GENERATION" ; // for return analysis |
972 | } |
973 | |
974 | std::string GenTableAccessorFuncReturnType(const FieldDef &field, |
975 | const std::string lifetime) { |
976 | const Type& type = field.value.type; |
977 | |
978 | switch (GetFullType(field.value.type)) { |
979 | case ftInteger: |
980 | case ftFloat: { |
981 | const auto typname = GetTypeBasic(type); |
982 | return typname; |
983 | } |
984 | case ftBool: { |
985 | return "bool" ; |
986 | } |
987 | case ftStruct: { |
988 | const auto typname = WrapInNameSpace(*type.struct_def); |
989 | return WrapInOptionIfNotRequired("&" + lifetime + " " + typname, field.required); |
990 | } |
991 | case ftTable: { |
992 | const auto typname = WrapInNameSpace(*type.struct_def); |
993 | return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">" , field.required); |
994 | } |
995 | case ftEnumKey: |
996 | case ftUnionKey: { |
997 | const auto typname = WrapInNameSpace(*type.enum_def); |
998 | return typname; |
999 | } |
1000 | |
1001 | case ftUnionValue: { |
1002 | return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">" , field.required); |
1003 | } |
1004 | case ftString: { |
1005 | return WrapInOptionIfNotRequired("&" + lifetime + " str" , field.required); |
1006 | } |
1007 | case ftVectorOfInteger: |
1008 | case ftVectorOfFloat: { |
1009 | const auto typname = GetTypeBasic(type.VectorType()); |
1010 | if (IsOneByte(type.VectorType().base_type)) { |
1011 | return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]" , field.required); |
1012 | } |
1013 | return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">" , field.required); |
1014 | } |
1015 | case ftVectorOfBool: { |
1016 | return WrapInOptionIfNotRequired("&" + lifetime + " [bool]" , field.required); |
1017 | } |
1018 | case ftVectorOfEnumKey: { |
1019 | const auto typname = WrapInNameSpace(*type.enum_def); |
1020 | return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">" , field.required); |
1021 | } |
1022 | case ftVectorOfStruct: { |
1023 | const auto typname = WrapInNameSpace(*type.struct_def); |
1024 | return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]" , field.required); |
1025 | } |
1026 | case ftVectorOfTable: { |
1027 | const auto typname = WrapInNameSpace(*type.struct_def); |
1028 | return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<" + \ |
1029 | typname + "<" + lifetime + ">>>" , field.required); |
1030 | } |
1031 | case ftVectorOfString: { |
1032 | return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<&" + \ |
1033 | lifetime + " str>>" , field.required); |
1034 | } |
1035 | case ftVectorOfUnionValue: { |
1036 | FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported" ); |
1037 | // TODO(rw): when we do support these, we should consider using the |
1038 | // Into trait to convert tables to typesafe union values. |
1039 | return "INVALID_CODE_GENERATION" ; // for return analysis |
1040 | } |
1041 | } |
1042 | return "INVALID_CODE_GENERATION" ; // for return analysis |
1043 | } |
1044 | |
1045 | std::string GenTableAccessorFuncBody(const FieldDef &field, |
1046 | const std::string lifetime, |
1047 | const std::string offset_prefix) { |
1048 | const std::string offset_name = offset_prefix + "::" + \ |
1049 | GetFieldOffsetName(field); |
1050 | const Type& type = field.value.type; |
1051 | |
1052 | switch (GetFullType(field.value.type)) { |
1053 | case ftInteger: |
1054 | case ftFloat: |
1055 | case ftBool: { |
1056 | const auto typname = GetTypeBasic(type); |
1057 | const auto default_value = GetDefaultScalarValue(field); |
1058 | return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + \ |
1059 | default_value + ")).unwrap()" ; |
1060 | } |
1061 | case ftStruct: { |
1062 | const auto typname = WrapInNameSpace(*type.struct_def); |
1063 | return AddUnwrapIfRequired("self._tab.get::<" + typname + ">(" + offset_name + ", None)" , field.required); |
1064 | } |
1065 | case ftTable: { |
1066 | const auto typname = WrapInNameSpace(*type.struct_def); |
1067 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" + \ |
1068 | typname + "<" + lifetime + ">>>(" + offset_name + ", None)" , field.required); |
1069 | } |
1070 | case ftUnionValue: { |
1071 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1072 | "flatbuffers::Table<" + lifetime + ">>>(" + offset_name + \ |
1073 | ", None)" , field.required); |
1074 | } |
1075 | case ftUnionKey: |
1076 | case ftEnumKey: { |
1077 | const auto underlying_typname = GetTypeBasic(type); |
1078 | const auto typname = WrapInNameSpace(*type.enum_def); |
1079 | const auto default_value = GetDefaultScalarValue(field); |
1080 | return "self._tab.get::<" + typname + ">(" + offset_name + \ |
1081 | ", Some(" + default_value + ")).unwrap()" ; |
1082 | } |
1083 | case ftString: { |
1084 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(" + \ |
1085 | offset_name + ", None)" , field.required); |
1086 | } |
1087 | |
1088 | case ftVectorOfInteger: |
1089 | case ftVectorOfFloat: { |
1090 | const auto typname = GetTypeBasic(type.VectorType()); |
1091 | std::string s = "self._tab.get::<flatbuffers::ForwardsUOffset<" |
1092 | "flatbuffers::Vector<" + lifetime + ", " + typname + \ |
1093 | ">>>(" + offset_name + ", None)" ; |
1094 | // single-byte values are safe to slice |
1095 | if (IsOneByte(type.VectorType().base_type)) { |
1096 | s += ".map(|v| v.safe_slice())" ; |
1097 | } |
1098 | return AddUnwrapIfRequired(s, field.required); |
1099 | } |
1100 | case ftVectorOfBool: { |
1101 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1102 | "flatbuffers::Vector<" + lifetime + ", bool>>>(" + \ |
1103 | offset_name + ", None).map(|v| v.safe_slice())" , field.required); |
1104 | } |
1105 | case ftVectorOfEnumKey: { |
1106 | const auto typname = WrapInNameSpace(*type.enum_def); |
1107 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1108 | "flatbuffers::Vector<" + lifetime + ", " + typname + ">>>(" + \ |
1109 | offset_name + ", None)" , field.required); |
1110 | } |
1111 | case ftVectorOfStruct: { |
1112 | const auto typname = WrapInNameSpace(*type.struct_def); |
1113 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1114 | "flatbuffers::Vector<" + typname + ">>>(" + \ |
1115 | offset_name + ", None).map(|v| v.safe_slice() )" , field.required); |
1116 | } |
1117 | case ftVectorOfTable: { |
1118 | const auto typname = WrapInNameSpace(*type.struct_def); |
1119 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1120 | "flatbuffers::Vector<flatbuffers::ForwardsUOffset<" + typname + \ |
1121 | "<" + lifetime + ">>>>>(" + offset_name + ", None)" , field.required); |
1122 | } |
1123 | case ftVectorOfString: { |
1124 | return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" |
1125 | "flatbuffers::Vector<flatbuffers::ForwardsUOffset<&" + \ |
1126 | lifetime + " str>>>>(" + offset_name + ", None)" , field.required); |
1127 | } |
1128 | case ftVectorOfUnionValue: { |
1129 | FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported" ); |
1130 | return "INVALID_CODE_GENERATION" ; // for return analysis |
1131 | } |
1132 | } |
1133 | return "INVALID_CODE_GENERATION" ; // for return analysis |
1134 | } |
1135 | |
1136 | bool TableFieldReturnsOption(const Type& type) { |
1137 | switch (GetFullType(type)) { |
1138 | case ftInteger: |
1139 | case ftFloat: |
1140 | case ftBool: |
1141 | case ftEnumKey: |
1142 | case ftUnionKey: |
1143 | return false; |
1144 | default: return true; |
1145 | } |
1146 | } |
1147 | |
1148 | // Generate an accessor struct, builder struct, and create function for a |
1149 | // table. |
1150 | void GenTable(const StructDef &struct_def) { |
1151 | code_.SetValue("STRUCT_NAME" , Name(struct_def)); |
1152 | code_.SetValue("OFFSET_TYPELABEL" , Name(struct_def) + "Offset" ); |
1153 | code_.SetValue("STRUCT_NAME_SNAKECASE" , MakeSnakeCase(Name(struct_def))); |
1154 | |
1155 | // Generate an offset type, the base type, the Follow impl, and the |
1156 | // init_from_table impl. |
1157 | code_ += "pub enum {{OFFSET_TYPELABEL}} {}" ; |
1158 | code_ += "#[derive(Copy, Clone, Debug, PartialEq)]" ; |
1159 | code_ += "" ; |
1160 | |
1161 | GenComment(struct_def.doc_comment); |
1162 | |
1163 | code_ += "pub struct {{STRUCT_NAME}}<'a> {" ; |
1164 | code_ += " pub _tab: flatbuffers::Table<'a>," ; |
1165 | code_ += "}" ; |
1166 | code_ += "" ; |
1167 | code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}}<'a> {" ; |
1168 | code_ += " type Inner = {{STRUCT_NAME}}<'a>;" ; |
1169 | code_ += " #[inline]" ; |
1170 | code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {" ; |
1171 | code_ += " Self {" ; |
1172 | code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc }," ; |
1173 | code_ += " }" ; |
1174 | code_ += " }" ; |
1175 | code_ += "}" ; |
1176 | code_ += "" ; |
1177 | code_ += "impl<'a> {{STRUCT_NAME}}<'a> {" ; |
1178 | code_ += " #[inline]" ; |
1179 | code_ += " pub fn init_from_table(table: flatbuffers::Table<'a>) -> " |
1180 | "Self {" ; |
1181 | code_ += " {{STRUCT_NAME}} {" ; |
1182 | code_ += " _tab: table," ; |
1183 | code_ += " }" ; |
1184 | code_ += " }" ; |
1185 | |
1186 | // Generate a convenient create* function that uses the above builder |
1187 | // to create a table in one function call. |
1188 | code_.SetValue("MAYBE_US" , |
1189 | struct_def.fields.vec.size() == 0 ? "_" : "" ); |
1190 | code_.SetValue("MAYBE_LT" , |
1191 | TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "" ); |
1192 | code_ += " #[allow(unused_mut)]" ; |
1193 | code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(" ; |
1194 | code_ += " _fbb: " |
1195 | "&'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>," ; |
1196 | code_ += " {{MAYBE_US}}args: &'args {{STRUCT_NAME}}Args{{MAYBE_LT}})" |
1197 | " -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'bldr>> {" ; |
1198 | |
1199 | code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);" ; |
1200 | for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; |
1201 | size; size /= 2) { |
1202 | for (auto it = struct_def.fields.vec.rbegin(); |
1203 | it != struct_def.fields.vec.rend(); ++it) { |
1204 | const auto &field = **it; |
1205 | // TODO(rw): fully understand this sortbysize usage |
1206 | if (!field.deprecated && (!struct_def.sortbysize || |
1207 | size == SizeOf(field.value.type.base_type))) { |
1208 | code_.SetValue("FIELD_NAME" , Name(field)); |
1209 | if (TableFieldReturnsOption(field.value.type)) { |
1210 | code_ += " if let Some(x) = args.{{FIELD_NAME}} " |
1211 | "{ builder.add_{{FIELD_NAME}}(x); }" ; |
1212 | } else { |
1213 | code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});" ; |
1214 | } |
1215 | } |
1216 | } |
1217 | } |
1218 | code_ += " builder.finish()" ; |
1219 | code_ += " }" ; |
1220 | code_ += "" ; |
1221 | |
1222 | // Generate field id constants. |
1223 | if (struct_def.fields.vec.size() > 0) { |
1224 | for (auto it = struct_def.fields.vec.begin(); |
1225 | it != struct_def.fields.vec.end(); ++it) { |
1226 | const auto &field = **it; |
1227 | if (field.deprecated) { |
1228 | // Deprecated fields won't be accessible. |
1229 | continue; |
1230 | } |
1231 | |
1232 | code_.SetValue("OFFSET_NAME" , GetFieldOffsetName(field)); |
1233 | code_.SetValue("OFFSET_VALUE" , NumToString(field.value.offset)); |
1234 | code_ += " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = " |
1235 | "{{OFFSET_VALUE}};" ; |
1236 | } |
1237 | code_ += "" ; |
1238 | } |
1239 | |
1240 | // Generate the accessors. Each has one of two forms: |
1241 | // |
1242 | // If a value can be None: |
1243 | // pub fn name(&'a self) -> Option<user_facing_type> { |
1244 | // self._tab.get::<internal_type>(offset, defaultval) |
1245 | // } |
1246 | // |
1247 | // If a value is always Some: |
1248 | // pub fn name(&'a self) -> user_facing_type { |
1249 | // self._tab.get::<internal_type>(offset, defaultval).unwrap() |
1250 | // } |
1251 | const auto offset_prefix = Name(struct_def); |
1252 | for (auto it = struct_def.fields.vec.begin(); |
1253 | it != struct_def.fields.vec.end(); ++it) { |
1254 | const auto &field = **it; |
1255 | if (field.deprecated) { |
1256 | // Deprecated fields won't be accessible. |
1257 | continue; |
1258 | } |
1259 | |
1260 | code_.SetValue("FIELD_NAME" , Name(field)); |
1261 | code_.SetValue("RETURN_TYPE" , |
1262 | GenTableAccessorFuncReturnType(field, "'a" )); |
1263 | code_.SetValue("FUNC_BODY" , |
1264 | GenTableAccessorFuncBody(field, "'a" , offset_prefix)); |
1265 | |
1266 | GenComment(field.doc_comment, " " ); |
1267 | code_ += " #[inline]" ; |
1268 | code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {" ; |
1269 | code_ += " {{FUNC_BODY}}" ; |
1270 | code_ += " }" ; |
1271 | |
1272 | // Generate a comparison function for this field if it is a key. |
1273 | if (field.key) { |
1274 | GenKeyFieldMethods(field); |
1275 | } |
1276 | |
1277 | // Generate a nested flatbuffer field, if applicable. |
1278 | auto nested = field.attributes.Lookup("nested_flatbuffer" ); |
1279 | if (nested) { |
1280 | std::string qualified_name = nested->constant; |
1281 | auto nested_root = parser_.LookupStruct(nested->constant); |
1282 | if (nested_root == nullptr) { |
1283 | qualified_name = parser_.current_namespace_->GetFullyQualifiedName( |
1284 | nested->constant); |
1285 | nested_root = parser_.LookupStruct(qualified_name); |
1286 | } |
1287 | FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser. |
1288 | (void)nested_root; |
1289 | |
1290 | code_.SetValue("OFFSET_NAME" , |
1291 | offset_prefix + "::" + GetFieldOffsetName(field)); |
1292 | code_ += " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> " |
1293 | " Option<{{STRUCT_NAME}}<'a>> {" ; |
1294 | code_ += " match self.{{FIELD_NAME}}() {" ; |
1295 | code_ += " None => { None }" ; |
1296 | code_ += " Some(data) => {" ; |
1297 | code_ += " use self::flatbuffers::Follow;" ; |
1298 | code_ += " Some(<flatbuffers::ForwardsUOffset" |
1299 | "<{{STRUCT_NAME}}<'a>>>::follow(data, 0))" ; |
1300 | code_ += " }," ; |
1301 | code_ += " }" ; |
1302 | code_ += " }" ; |
1303 | } |
1304 | } |
1305 | |
1306 | // Explicit specializations for union accessors |
1307 | for (auto it = struct_def.fields.vec.begin(); |
1308 | it != struct_def.fields.vec.end(); ++it) { |
1309 | const auto &field = **it; |
1310 | if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) { |
1311 | continue; |
1312 | } |
1313 | |
1314 | auto u = field.value.type.enum_def; |
1315 | |
1316 | code_.SetValue("FIELD_NAME" , Name(field)); |
1317 | |
1318 | for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) { |
1319 | auto &ev = **u_it; |
1320 | if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; } |
1321 | |
1322 | auto table_init_type = WrapInNameSpace( |
1323 | ev.union_type.struct_def->defined_namespace, |
1324 | ev.union_type.struct_def->name); |
1325 | |
1326 | code_.SetValue("U_ELEMENT_ENUM_TYPE" , |
1327 | WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev))); |
1328 | code_.SetValue("U_ELEMENT_TABLE_TYPE" , table_init_type); |
1329 | code_.SetValue("U_ELEMENT_NAME" , MakeSnakeCase(Name(ev))); |
1330 | |
1331 | code_ += " #[inline]" ; |
1332 | code_ += " #[allow(non_snake_case)]" ; |
1333 | code_ += " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> " |
1334 | "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {" ; |
1335 | code_ += " if self.{{FIELD_NAME}}_type() == {{U_ELEMENT_ENUM_TYPE}} {" ; |
1336 | code_ += " self.{{FIELD_NAME}}().map(|u| " |
1337 | "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))" ; |
1338 | code_ += " } else {" ; |
1339 | code_ += " None" ; |
1340 | code_ += " }" ; |
1341 | code_ += " }" ; |
1342 | code_ += "" ; |
1343 | } |
1344 | } |
1345 | |
1346 | code_ += "}" ; // End of table impl. |
1347 | code_ += "" ; |
1348 | |
1349 | // Generate an args struct: |
1350 | code_.SetValue("MAYBE_LT" , |
1351 | TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "" ); |
1352 | code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {" ; |
1353 | for (auto it = struct_def.fields.vec.begin(); |
1354 | it != struct_def.fields.vec.end(); ++it) { |
1355 | const auto &field = **it; |
1356 | if (!field.deprecated) { |
1357 | code_.SetValue("PARAM_NAME" , Name(field)); |
1358 | code_.SetValue("PARAM_TYPE" , TableBuilderArgsDefnType(field, "'a " )); |
1359 | code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}}," ; |
1360 | } |
1361 | } |
1362 | code_ += "}" ; |
1363 | |
1364 | // Generate an impl of Default for the *Args type: |
1365 | code_ += "impl<'a> Default for {{STRUCT_NAME}}Args{{MAYBE_LT}} {" ; |
1366 | code_ += " #[inline]" ; |
1367 | code_ += " fn default() -> Self {" ; |
1368 | code_ += " {{STRUCT_NAME}}Args {" ; |
1369 | for (auto it = struct_def.fields.vec.begin(); |
1370 | it != struct_def.fields.vec.end(); ++it) { |
1371 | const auto &field = **it; |
1372 | if (!field.deprecated) { |
1373 | code_.SetValue("PARAM_VALUE" , TableBuilderArgsDefaultValue(field)); |
1374 | code_.SetValue("REQ" , field.required ? " // required field" : "" ); |
1375 | code_.SetValue("PARAM_NAME" , Name(field)); |
1376 | code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}" ; |
1377 | } |
1378 | } |
1379 | code_ += " }" ; |
1380 | code_ += " }" ; |
1381 | code_ += "}" ; |
1382 | |
1383 | // Generate a builder struct: |
1384 | code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {" ; |
1385 | code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>," ; |
1386 | code_ += " start_: flatbuffers::WIPOffset<" |
1387 | "flatbuffers::TableUnfinishedWIPOffset>," ; |
1388 | code_ += "}" ; |
1389 | |
1390 | // Generate builder functions: |
1391 | code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {" ; |
1392 | for (auto it = struct_def.fields.vec.begin(); |
1393 | it != struct_def.fields.vec.end(); ++it) { |
1394 | const auto &field = **it; |
1395 | if (!field.deprecated) { |
1396 | const bool is_scalar = IsScalar(field.value.type.base_type); |
1397 | |
1398 | std::string offset = GetFieldOffsetName(field); |
1399 | std::string name = Name(field); |
1400 | std::string value = GetDefaultScalarValue(field); |
1401 | |
1402 | // Generate functions to add data, which take one of two forms. |
1403 | // |
1404 | // If a value has a default: |
1405 | // fn add_x(x_: type) { |
1406 | // fbb_.push_slot::<type>(offset, x_, Some(default)); |
1407 | // } |
1408 | // |
1409 | // If a value does not have a default: |
1410 | // fn add_x(x_: type) { |
1411 | // fbb_.push_slot_always::<type>(offset, x_); |
1412 | // } |
1413 | code_.SetValue("FIELD_NAME" , Name(field)); |
1414 | code_.SetValue("FIELD_OFFSET" , Name(struct_def) + "::" + offset); |
1415 | code_.SetValue("FIELD_TYPE" , TableBuilderArgsAddFuncType(field, "'b " )); |
1416 | code_.SetValue("FUNC_BODY" , TableBuilderArgsAddFuncBody(field)); |
1417 | code_ += " #[inline]" ; |
1418 | code_ += " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: " |
1419 | "{{FIELD_TYPE}}) {" ; |
1420 | if (is_scalar) { |
1421 | code_.SetValue("FIELD_DEFAULT_VALUE" , |
1422 | TableBuilderAddFuncDefaultValue(field)); |
1423 | code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, " |
1424 | "{{FIELD_DEFAULT_VALUE}});" ; |
1425 | } else { |
1426 | code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});" ; |
1427 | } |
1428 | code_ += " }" ; |
1429 | } |
1430 | } |
1431 | |
1432 | // Struct initializer (all fields required); |
1433 | code_ += " #[inline]" ; |
1434 | code_ += |
1435 | " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> " |
1436 | "{{STRUCT_NAME}}Builder<'a, 'b> {" ; |
1437 | code_.SetValue("NUM_FIELDS" , NumToString(struct_def.fields.vec.size())); |
1438 | code_ += " let start = _fbb.start_table();" ; |
1439 | code_ += " {{STRUCT_NAME}}Builder {" ; |
1440 | code_ += " fbb_: _fbb," ; |
1441 | code_ += " start_: start," ; |
1442 | code_ += " }" ; |
1443 | code_ += " }" ; |
1444 | |
1445 | // finish() function. |
1446 | code_ += " #[inline]" ; |
1447 | code_ += " pub fn finish(self) -> " |
1448 | "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {" ; |
1449 | code_ += " let o = self.fbb_.end_table(self.start_);" ; |
1450 | |
1451 | for (auto it = struct_def.fields.vec.begin(); |
1452 | it != struct_def.fields.vec.end(); ++it) { |
1453 | const auto &field = **it; |
1454 | if (!field.deprecated && field.required) { |
1455 | code_.SetValue("FIELD_NAME" , MakeSnakeCase(Name(field))); |
1456 | code_.SetValue("OFFSET_NAME" , GetFieldOffsetName(field)); |
1457 | code_ += " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}}," |
1458 | "\"{{FIELD_NAME}}\");" ; |
1459 | } |
1460 | } |
1461 | code_ += " flatbuffers::WIPOffset::new(o.value())" ; |
1462 | code_ += " }" ; |
1463 | code_ += "}" ; |
1464 | code_ += "" ; |
1465 | } |
1466 | |
1467 | // Generate functions to compare tables and structs by key. This function |
1468 | // must only be called if the field key is defined. |
1469 | void GenKeyFieldMethods(const FieldDef &field) { |
1470 | FLATBUFFERS_ASSERT(field.key); |
1471 | |
1472 | code_.SetValue("KEY_TYPE" , GenTableAccessorFuncReturnType(field, "" )); |
1473 | |
1474 | code_ += " #[inline]" ; |
1475 | code_ += " pub fn key_compare_less_than(&self, o: &{{STRUCT_NAME}}) -> " |
1476 | " bool {" ; |
1477 | code_ += " self.{{FIELD_NAME}}() < o.{{FIELD_NAME}}()" ; |
1478 | code_ += " }" ; |
1479 | code_ += "" ; |
1480 | code_ += " #[inline]" ; |
1481 | code_ += " pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> " |
1482 | " ::std::cmp::Ordering {" ; |
1483 | code_ += " let key = self.{{FIELD_NAME}}();" ; |
1484 | code_ += " key.cmp(&val)" ; |
1485 | code_ += " }" ; |
1486 | } |
1487 | |
1488 | // Generate functions for accessing the root table object. This function |
1489 | // must only be called if the root table is defined. |
1490 | void GenRootTableFuncs(const StructDef &struct_def) { |
1491 | FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined" ); |
1492 | auto name = Name(struct_def); |
1493 | |
1494 | code_.SetValue("STRUCT_NAME" , name); |
1495 | code_.SetValue("STRUCT_NAME_SNAKECASE" , MakeSnakeCase(name)); |
1496 | code_.SetValue("STRUCT_NAME_CAPS" , MakeUpper(MakeSnakeCase(name))); |
1497 | |
1498 | // The root datatype accessors: |
1499 | code_ += "#[inline]" ; |
1500 | code_ += |
1501 | "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])" |
1502 | " -> {{STRUCT_NAME}}<'a> {" ; |
1503 | code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)" ; |
1504 | code_ += "}" ; |
1505 | code_ += "" ; |
1506 | |
1507 | code_ += "#[inline]" ; |
1508 | code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}" |
1509 | "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {" ; |
1510 | code_ += " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>" |
1511 | "(buf)" ; |
1512 | code_ += "}" ; |
1513 | code_ += "" ; |
1514 | |
1515 | if (parser_.file_identifier_.length()) { |
1516 | // Declare the identifier |
1517 | code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\" ; |
1518 | code_ += " = \"" + parser_.file_identifier_ + "\";" ; |
1519 | code_ += "" ; |
1520 | |
1521 | // Check if a buffer has the identifier. |
1522 | code_ += "#[inline]" ; |
1523 | code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\" ; |
1524 | code_ += "(buf: &[u8]) -> bool {" ; |
1525 | code_ += " return flatbuffers::buffer_has_identifier(buf, \\" ; |
1526 | code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);" ; |
1527 | code_ += "}" ; |
1528 | code_ += "" ; |
1529 | code_ += "#[inline]" ; |
1530 | code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\" ; |
1531 | code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {" ; |
1532 | code_ += " return flatbuffers::buffer_has_identifier(buf, \\" ; |
1533 | code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);" ; |
1534 | code_ += "}" ; |
1535 | code_ += "" ; |
1536 | } |
1537 | |
1538 | if (parser_.file_extension_.length()) { |
1539 | // Return the extension |
1540 | code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\" ; |
1541 | code_ += "\"" + parser_.file_extension_ + "\";" ; |
1542 | code_ += "" ; |
1543 | } |
1544 | |
1545 | // Finish a buffer with a given root object: |
1546 | code_.SetValue("OFFSET_TYPELABEL" , Name(struct_def) + "Offset" ); |
1547 | code_ += "#[inline]" ; |
1548 | code_ += "pub fn finish_{{STRUCT_NAME_SNAKECASE}}_buffer<'a, 'b>(" ; |
1549 | code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>," ; |
1550 | code_ += " root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {" ; |
1551 | if (parser_.file_identifier_.length()) { |
1552 | code_ += " fbb.finish(root, Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));" ; |
1553 | } else { |
1554 | code_ += " fbb.finish(root, None);" ; |
1555 | } |
1556 | code_ += "}" ; |
1557 | code_ += "" ; |
1558 | code_ += "#[inline]" ; |
1559 | code_ += "pub fn finish_size_prefixed_{{STRUCT_NAME_SNAKECASE}}_buffer" |
1560 | "<'a, 'b>(" |
1561 | "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, " |
1562 | "root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {" ; |
1563 | if (parser_.file_identifier_.length()) { |
1564 | code_ += " fbb.finish_size_prefixed(root, " |
1565 | "Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));" ; |
1566 | } else { |
1567 | code_ += " fbb.finish_size_prefixed(root, None);" ; |
1568 | } |
1569 | code_ += "}" ; |
1570 | } |
1571 | |
1572 | static void GenPadding( |
1573 | const FieldDef &field, std::string *code_ptr, int *id, |
1574 | const std::function<void(int bits, std::string *code_ptr, int *id)> &f) { |
1575 | if (field.padding) { |
1576 | for (int i = 0; i < 4; i++) { |
1577 | if (static_cast<int>(field.padding) & (1 << i)) { |
1578 | f((1 << i) * 8, code_ptr, id); |
1579 | } |
1580 | } |
1581 | assert(!(field.padding & ~0xF)); |
1582 | } |
1583 | } |
1584 | |
1585 | static void PaddingDefinition(int bits, std::string *code_ptr, int *id) { |
1586 | *code_ptr += " padding" + NumToString((*id)++) + "__: u" + \ |
1587 | NumToString(bits) + "," ; |
1588 | } |
1589 | |
1590 | static void PaddingInitializer(int bits, std::string *code_ptr, int *id) { |
1591 | (void)bits; |
1592 | *code_ptr += "padding" + NumToString((*id)++) + "__: 0," ; |
1593 | } |
1594 | |
1595 | // Generate an accessor struct with constructor for a flatbuffers struct. |
1596 | void GenStruct(const StructDef &struct_def) { |
1597 | // Generates manual padding and alignment. |
1598 | // Variables are private because they contain little endian data on all |
1599 | // platforms. |
1600 | GenComment(struct_def.doc_comment); |
1601 | code_.SetValue("ALIGN" , NumToString(struct_def.minalign)); |
1602 | code_.SetValue("STRUCT_NAME" , Name(struct_def)); |
1603 | |
1604 | code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}" ; |
1605 | code_ += "#[repr(C, align({{ALIGN}}))]" ; |
1606 | |
1607 | // PartialEq is useful to derive because we can correctly compare structs |
1608 | // for equality by just comparing their underlying byte data. This doesn't |
1609 | // hold for PartialOrd/Ord. |
1610 | code_ += "#[derive(Clone, Copy, Debug, PartialEq)]" ; |
1611 | code_ += "pub struct {{STRUCT_NAME}} {" ; |
1612 | |
1613 | int padding_id = 0; |
1614 | for (auto it = struct_def.fields.vec.begin(); |
1615 | it != struct_def.fields.vec.end(); ++it) { |
1616 | const auto &field = **it; |
1617 | code_.SetValue("FIELD_TYPE" , GetTypeGet(field.value.type)); |
1618 | code_.SetValue("FIELD_NAME" , Name(field)); |
1619 | code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}}," ; |
1620 | |
1621 | if (field.padding) { |
1622 | std::string padding; |
1623 | GenPadding(field, &padding, &padding_id, PaddingDefinition); |
1624 | code_ += padding; |
1625 | } |
1626 | } |
1627 | |
1628 | code_ += "} // pub struct {{STRUCT_NAME}}" ; |
1629 | |
1630 | // Generate impls for SafeSliceAccess (because all structs are endian-safe), |
1631 | // Follow for the value type, Follow for the reference type, Push for the |
1632 | // value type, and Push for the reference type. |
1633 | code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}" ; |
1634 | code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {" ; |
1635 | code_ += " type Inner = &'a {{STRUCT_NAME}};" ; |
1636 | code_ += " #[inline]" ; |
1637 | code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {" ; |
1638 | code_ += " <&'a {{STRUCT_NAME}}>::follow(buf, loc)" ; |
1639 | code_ += " }" ; |
1640 | code_ += "}" ; |
1641 | code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_NAME}} {" ; |
1642 | code_ += " type Inner = &'a {{STRUCT_NAME}};" ; |
1643 | code_ += " #[inline]" ; |
1644 | code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {" ; |
1645 | code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_NAME}}>(buf, loc)" ; |
1646 | code_ += " }" ; |
1647 | code_ += "}" ; |
1648 | code_ += "impl<'b> flatbuffers::Push for {{STRUCT_NAME}} {" ; |
1649 | code_ += " type Output = {{STRUCT_NAME}};" ; |
1650 | code_ += " #[inline]" ; |
1651 | code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {" ; |
1652 | code_ += " let src = unsafe {" ; |
1653 | code_ += " ::std::slice::from_raw_parts(" |
1654 | "self as *const {{STRUCT_NAME}} as *const u8, Self::size())" ; |
1655 | code_ += " };" ; |
1656 | code_ += " dst.copy_from_slice(src);" ; |
1657 | code_ += " }" ; |
1658 | code_ += "}" ; |
1659 | code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_NAME}} {" ; |
1660 | code_ += " type Output = {{STRUCT_NAME}};" ; |
1661 | code_ += "" ; |
1662 | code_ += " #[inline]" ; |
1663 | code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {" ; |
1664 | code_ += " let src = unsafe {" ; |
1665 | code_ += " ::std::slice::from_raw_parts(" |
1666 | "*self as *const {{STRUCT_NAME}} as *const u8, Self::size())" ; |
1667 | code_ += " };" ; |
1668 | code_ += " dst.copy_from_slice(src);" ; |
1669 | code_ += " }" ; |
1670 | code_ += "}" ; |
1671 | code_ += "" ; |
1672 | code_ += "" ; |
1673 | |
1674 | // Generate a constructor that takes all fields as arguments. |
1675 | code_ += "impl {{STRUCT_NAME}} {" ; |
1676 | std::string arg_list; |
1677 | std::string init_list; |
1678 | padding_id = 0; |
1679 | for (auto it = struct_def.fields.vec.begin(); |
1680 | it != struct_def.fields.vec.end(); ++it) { |
1681 | const auto &field = **it; |
1682 | const auto member_name = Name(field) + "_" ; |
1683 | const auto reference = StructMemberAccessNeedsCopy(field.value.type) |
1684 | ? "" : "&'a " ; |
1685 | const auto arg_name = "_" + Name(field); |
1686 | const auto arg_type = reference + GetTypeGet(field.value.type); |
1687 | |
1688 | if (it != struct_def.fields.vec.begin()) { |
1689 | arg_list += ", " ; |
1690 | } |
1691 | arg_list += arg_name + ": " ; |
1692 | arg_list += arg_type; |
1693 | init_list += " " + member_name; |
1694 | if (StructMemberAccessNeedsCopy(field.value.type)) { |
1695 | init_list += ": " + arg_name + ".to_little_endian(),\n" ; |
1696 | } else { |
1697 | init_list += ": *" + arg_name + ",\n" ; |
1698 | } |
1699 | } |
1700 | |
1701 | code_.SetValue("ARG_LIST" , arg_list); |
1702 | code_.SetValue("INIT_LIST" , init_list); |
1703 | code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {" ; |
1704 | code_ += " {{STRUCT_NAME}} {" ; |
1705 | code_ += "{{INIT_LIST}}" ; |
1706 | padding_id = 0; |
1707 | for (auto it = struct_def.fields.vec.begin(); |
1708 | it != struct_def.fields.vec.end(); ++it) { |
1709 | const auto &field = **it; |
1710 | if (field.padding) { |
1711 | std::string padding; |
1712 | GenPadding(field, &padding, &padding_id, PaddingInitializer); |
1713 | code_ += " " + padding; |
1714 | } |
1715 | } |
1716 | code_ += " }" ; |
1717 | code_ += " }" ; |
1718 | |
1719 | // Generate accessor methods for the struct. |
1720 | for (auto it = struct_def.fields.vec.begin(); |
1721 | it != struct_def.fields.vec.end(); ++it) { |
1722 | const auto &field = **it; |
1723 | |
1724 | auto field_type = TableBuilderArgsAddFuncType(field, "'a" ); |
1725 | auto member = "self." + Name(field) + "_" ; |
1726 | auto value = StructMemberAccessNeedsCopy(field.value.type) ? |
1727 | member + ".from_little_endian()" : member; |
1728 | |
1729 | code_.SetValue("FIELD_NAME" , Name(field)); |
1730 | code_.SetValue("FIELD_TYPE" , field_type); |
1731 | code_.SetValue("FIELD_VALUE" , value); |
1732 | code_.SetValue("REF" , IsStruct(field.value.type) ? "&" : "" ); |
1733 | |
1734 | GenComment(field.doc_comment, " " ); |
1735 | code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {" ; |
1736 | code_ += " {{REF}}{{FIELD_VALUE}}" ; |
1737 | code_ += " }" ; |
1738 | |
1739 | // Generate a comparison function for this field if it is a key. |
1740 | if (field.key) { |
1741 | GenKeyFieldMethods(field); |
1742 | } |
1743 | } |
1744 | code_ += "}" ; |
1745 | code_ += "" ; |
1746 | } |
1747 | |
1748 | void GenNamespaceImports(const int white_spaces) { |
1749 | std::string indent = std::string(white_spaces, ' '); |
1750 | code_ += "" ; |
1751 | code_ += indent + "use std::mem;" ; |
1752 | code_ += indent + "use std::cmp::Ordering;" ; |
1753 | code_ += "" ; |
1754 | code_ += indent + "extern crate flatbuffers;" ; |
1755 | code_ += indent + "use self::flatbuffers::EndianScalar;" ; |
1756 | } |
1757 | |
1758 | // Set up the correct namespace. This opens a namespace if the current |
1759 | // namespace is different from the target namespace. This function |
1760 | // closes and opens the namespaces only as necessary. |
1761 | // |
1762 | // The file must start and end with an empty (or null) namespace so that |
1763 | // namespaces are properly opened and closed. |
1764 | void SetNameSpace(const Namespace *ns) { |
1765 | if (cur_name_space_ == ns) { return; } |
1766 | |
1767 | // Compute the size of the longest common namespace prefix. |
1768 | // If cur_name_space is A::B::C::D and ns is A::B::E::F::G, |
1769 | // the common prefix is A::B:: and we have old_size = 4, new_size = 5 |
1770 | // and common_prefix_size = 2 |
1771 | size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0; |
1772 | size_t new_size = ns ? ns->components.size() : 0; |
1773 | |
1774 | size_t common_prefix_size = 0; |
1775 | while (common_prefix_size < old_size && common_prefix_size < new_size && |
1776 | ns->components[common_prefix_size] == |
1777 | cur_name_space_->components[common_prefix_size]) { |
1778 | common_prefix_size++; |
1779 | } |
1780 | |
1781 | // Close cur_name_space in reverse order to reach the common prefix. |
1782 | // In the previous example, D then C are closed. |
1783 | for (size_t j = old_size; j > common_prefix_size; --j) { |
1784 | code_ += "} // pub mod " + cur_name_space_->components[j - 1]; |
1785 | } |
1786 | if (old_size != common_prefix_size) { code_ += "" ; } |
1787 | |
1788 | // open namespace parts to reach the ns namespace |
1789 | // in the previous example, E, then F, then G are opened |
1790 | for (auto j = common_prefix_size; j != new_size; ++j) { |
1791 | code_ += "#[allow(unused_imports, dead_code)]" ; |
1792 | code_ += "pub mod " + MakeSnakeCase(ns->components[j]) + " {" ; |
1793 | // Generate local namespace imports. |
1794 | GenNamespaceImports(2); |
1795 | } |
1796 | if (new_size != common_prefix_size) { code_ += "" ; } |
1797 | |
1798 | cur_name_space_ = ns; |
1799 | } |
1800 | }; |
1801 | |
1802 | } // namespace rust |
1803 | |
1804 | bool GenerateRust(const Parser &parser, const std::string &path, |
1805 | const std::string &file_name) { |
1806 | rust::RustGenerator generator(parser, path, file_name); |
1807 | return generator.generate(); |
1808 | } |
1809 | |
1810 | std::string RustMakeRule(const Parser &parser, const std::string &path, |
1811 | const std::string &file_name) { |
1812 | std::string filebase = |
1813 | flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); |
1814 | std::string make_rule = GeneratedFileName(path, filebase) + ": " ; |
1815 | |
1816 | auto included_files = parser.GetIncludedFilesRecursive(file_name); |
1817 | for (auto it = included_files.begin(); it != included_files.end(); ++it) { |
1818 | make_rule += " " + *it; |
1819 | } |
1820 | return make_rule; |
1821 | } |
1822 | |
1823 | } // namespace flatbuffers |
1824 | |
1825 | // TODO(rw): Generated code should import other generated files. |
1826 | // TODO(rw): Generated code should refer to namespaces in included files in a |
1827 | // way that makes them referrable. |
1828 | // TODO(rw): Generated code should indent according to nesting level. |
1829 | // TODO(rw): Generated code should generate endian-safe Debug impls. |
1830 | // TODO(rw): Generated code could use a Rust-only enum type to access unions, |
1831 | // instead of making the user use _type() to manually switch. |
1832 | |