Implement Serialize on generated rust types (#7022)

* fix for rust build

* Rust: Implement Serialize on generated types

For debugging convenience it is really handy to be able to dump out
types as another format (ie: json). For example, if we are logging a
type to a structured logging system, or even printing it out in a
structured way to the console.

Right now we handle this by shelling out to `flatc` which is not ideal;
by implementing Serialize on the generated types we can use any of the
Serializer-implementing packages for our structured debug output.

* clang-format

* Make the flatbuffers Rust crate only have an optional dependency on the `serde` packages.

* fix warning

* fix rust test build

* Oh yeah this needs to be initialized

* fix toml syntax

* code review feedback

* rebuild test data
This commit is contained in:
Max Burke
2022-01-30 16:29:18 -08:00
committed by GitHub
parent dd8fccfb1b
commit 1d294a31b8
67 changed files with 5455 additions and 1 deletions

View File

@@ -384,6 +384,11 @@ class RustGenerator : public BaseGenerator {
code_ += "extern crate flatbuffers;";
code_ += "use std::mem;";
code_ += "use std::cmp::Ordering;";
if (parser_.opts.rust_serialize) {
code_ += "extern crate serde;";
code_ +=
"use self::serde::ser::{Serialize, Serializer, SerializeStruct};";
}
code_ += "use self::flatbuffers::{EndianScalar, Follow};";
code_ += "use super::*;";
cur_name_space_ = symbol.defined_namespace;
@@ -842,6 +847,26 @@ class RustGenerator : public BaseGenerator {
code_.SetValue("INTO_BASE", "self.0");
}
// Implement serde::Serialize
if (parser_.opts.rust_serialize) {
code_ += "impl Serialize for {{ENUM_NAME}} {";
code_ +=
" fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>";
code_ += " where";
code_ += " S: Serializer,";
code_ += " {";
if (IsBitFlagsEnum(enum_def)) {
code_ += " serializer.serialize_u32(self.bits() as u32)";
} else {
code_ +=
" serializer.serialize_unit_variant(\"{{ENUM_NAME}}\", self.0 as "
"u32, self.variant_name().unwrap())";
}
code_ += " }";
code_ += "}";
code_ += "";
}
// Generate Follow and Push so we can serialize and stuff.
code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {";
code_ += " type Inner = Self;";
@@ -2005,6 +2030,74 @@ class RustGenerator : public BaseGenerator {
code_ += " }";
code_ += " }";
code_ += "}";
code_ += "";
// Implement serde::Serialize
if (parser_.opts.rust_serialize) {
const auto numFields = struct_def.fields.vec.size();
code_.SetValue("NUM_FIELDS", NumToString(numFields));
code_ += "impl Serialize for {{STRUCT_NAME}}<'_> {";
code_ +=
" fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>";
code_ += " where";
code_ += " S: Serializer,";
code_ += " {";
if (numFields == 0) {
code_ +=
" let s = serializer.serialize_struct(\"{{STRUCT_NAME}}\", 0)?;";
} else {
code_ +=
" let mut s = serializer.serialize_struct(\"{{STRUCT_NAME}}\", "
"{{NUM_FIELDS}})?;";
}
ForAllTableFields(struct_def, [&](const FieldDef &field) {
const Type &type = field.value.type;
if (IsUnion(type)) {
if (type.base_type == BASE_TYPE_UNION) {
const auto &enum_def = *type.enum_def;
code_.SetValue("ENUM_NAME", WrapInNameSpace(enum_def));
code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name);
code_ += " match self.{{FIELD_TYPE_FIELD_NAME}}_type() {";
code_ += " {{ENUM_NAME}}::NONE => (),";
ForAllUnionObjectVariantsBesidesNone(enum_def, [&] {
code_.SetValue("FIELD_TYPE_FIELD_NAME", field.name);
code_ += " {{ENUM_NAME}}::{{VARIANT_NAME}} => {";
code_ +=
" let f = "
"self.{{FIELD_TYPE_FIELD_NAME}}_as_{{U_ELEMENT_NAME}}()";
code_ +=
" .expect(\"Invalid union table, expected "
"`{{ENUM_NAME}}::{{VARIANT_NAME}}`.\");";
code_ += " s.serialize_field(\"{{FIELD_NAME}}\", &f)?;";
code_ += " }";
});
code_ += " _ => unimplemented!(),";
code_ += " }";
} else {
code_ +=
" s.serialize_field(\"{{FIELD_NAME}}\", "
"&self.{{FIELD_NAME}}())?;";
}
} else {
if (field.IsOptional()) {
code_ += " if let Some(f) = self.{{FIELD_NAME}}() {";
code_ += " s.serialize_field(\"{{FIELD_NAME}}\", &f)?;";
code_ += " } else {";
code_ += " s.skip_field(\"{{FIELD_NAME}}\")?;";
code_ += " }";
} else {
code_ +=
" s.serialize_field(\"{{FIELD_NAME}}\", "
"&self.{{FIELD_NAME}}())?;";
}
}
});
code_ += " s.end()";
code_ += " }";
code_ += "}";
code_ += "";
}
// Generate a builder struct:
code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {";
@@ -2636,6 +2729,36 @@ class RustGenerator : public BaseGenerator {
code_ += " v.in_buffer::<Self>(pos)";
code_ += " }";
code_ += "}";
code_ += "";
// Implement serde::Serialize
if (parser_.opts.rust_serialize) {
const auto numFields = struct_def.fields.vec.size();
code_.SetValue("NUM_FIELDS", NumToString(numFields));
code_ += "impl Serialize for {{STRUCT_NAME}} {";
code_ +=
" fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>";
code_ += " where";
code_ += " S: Serializer,";
code_ += " {";
if (numFields == 0) {
code_ +=
" let s = serializer.serialize_struct(\"{{STRUCT_NAME}}\", 0)?;";
} else {
code_ +=
" let mut s = serializer.serialize_struct(\"{{STRUCT_NAME}}\", "
"{{NUM_FIELDS}})?;";
}
ForAllStructFields(struct_def, [&](const FieldDef &unused) {
(void)unused;
code_ +=
" s.serialize_field(\"{{FIELD_NAME}}\", &self.{{FIELD_NAME}}())?;";
});
code_ += " s.end()";
code_ += " }";
code_ += "}";
code_ += "";
}
// Generate a constructor that takes all fields as arguments.
code_ += "impl<'a> {{STRUCT_NAME}} {";
@@ -2834,6 +2957,12 @@ class RustGenerator : public BaseGenerator {
code_ += indent + "use std::mem;";
code_ += indent + "use std::cmp::Ordering;";
code_ += "";
if (parser_.opts.rust_serialize) {
code_ += indent + "extern crate serde;";
code_ += indent +
"use self::serde::ser::{Serialize, Serializer, SerializeStruct};";
code_ += "";
}
code_ += indent + "extern crate flatbuffers;";
code_ += indent + "use self::flatbuffers::{EndianScalar, Follow};";
}