rust-protobuf icon indicating copy to clipboard operation
rust-protobuf copied to clipboard

Customized generation of sub-modules

Open neilisaac opened this issue 2 years ago • 1 comments

generated rust stubs for oneof and nested messages generate sub-modules to namespace the types, however the generated structs and enums in the sub-modules aren't customized.

For example

message ExampleMessageWithOneof {
    oneof selection {
        string string_field = 1;
        bool bool_field = 2;
        int32 int_field = 3;
        ExampleTopLevelEnum enum_field = 4;
        ExampleMessageWithNestedMessage message_field = 5;
        google.protobuf.Empty empty_field = 6;
    }
}

with

impl CustomizeCallback for SerdeSerializeDeserialize {
    fn message(&self, _message: &MessageDescriptor) -> Customize {
        Customize::default().before("#[derive(::serde::Serialize, ::serde::Deserialize)]")
    }

    fn enumeration(&self, _enum: &EnumDescriptor) -> Customize {
        Customize::default().before("#[derive(::serde::Serialize, ::serde::Deserialize)]")
    }

    fn field(&self, field: &FieldDescriptor) -> Customize {
        match field.proto().type_() {
            Type::TYPE_ENUM => {
                Customize::default().before(
                    "#[serde(serialize_with = \"::protobuf_serde_helpers::serialize_enum_or_unknown\", deserialize_with = \"::protobuf_serde_helpers::deserialize_enum_or_unknown\")]")
            }
            // refer to https://serde.rs/remote-derive.html
            Type::TYPE_MESSAGE => Customize::default().before("#[serde(with=\"::protobuf_serde_helpers::MessageFieldDef\")]"),
            _ => Customize::default(),
        }
    }

    fn special_field(&self, _message: &MessageDescriptor, _field: &str) -> Customize {
        Customize::default().before("#[serde(skip)]")
    }
}

generates

#[derive(PartialEq,Clone,Default,Debug)]
#[derive(::serde::Serialize, ::serde::Deserialize)]
// @@protoc_insertion_point(message:x.proto.ExampleMessageWithOneof)
pub struct ExampleMessageWithOneof {
    // message oneof groups
    pub selection: ::std::option::Option<example_message_with_oneof::Selection>,
    // special fields
    #[serde(skip)]
    // @@protoc_insertion_point(special_field:x.proto.ExampleMessageWithOneof.special_fields)
    pub special_fields: ::protobuf::SpecialFields,
}

pub mod example_message_with_oneof {

    #[derive(Clone,PartialEq,Debug)]
    #[non_exhaustive]
    // @@protoc_insertion_point(oneof:x.proto.ExampleMessageWithOneof.selection)
    pub enum Selection {
        // @@protoc_insertion_point(oneof_field:x.proto.ExampleMessageWithOneof.string_field)
        StringField(::std::string::String),
        // @@protoc_insertion_point(oneof_field:x.proto.ExampleMessageWithOneof.bool_field)
        BoolField(bool),
        // @@protoc_insertion_point(oneof_field:x.proto.ExampleMessageWithOneof.int_field)
        IntField(i32),
        // @@protoc_insertion_point(oneof_field:x.proto.ExampleMessageWithOneof.enum_field)
        EnumField(::protobuf::EnumOrUnknown<super::ExampleTopLevelEnum>),
        // @@protoc_insertion_point(oneof_field:x.proto.ExampleMessageWithOneof.message_field)
        MessageField(super::ExampleMessageWithNestedMessage),
    }
...
}

which fails with

error[E0277]: the trait bound `Selection: Deserialize<'_>` is not satisfied
    --> x/proto/example_rust_proto_pb_rs/example.rs:1048:5
     |
1048 |     pub selection: ::std::option::Option<example_message_with_oneof::Selection>,
     |     ^^^ the trait `Deserialize<'_>` is not implemented for `Selection`
     |
     = note: required because of the requirements on the impl of `Deserialize<'_>` for `std::option::Option<Selection>`

There are similar issues with nested messages and enums:

message ExampleMessageWithNestedMessage {
    message ExampleNestedMessage {
        int32 value = 1;
    }

    ExampleNestedMessage nested_message = 1;
}

message ExampleMessageWithEnum {
    enum ExampleNestedEnum {
        EXAMPLE_NESTED_ENUM_UNSPECIFIED = 0;
        EXAMPLE_NESTED_ENUM_A = 1;
    }

    ExampleTopLevelEnum enum1 = 1;
    ExampleNestedEnum enum2 = 2;
}

neilisaac avatar Jun 07 '22 15:06 neilisaac

From my experience, I just add definition of oneof into your CustomizeCallback struct in build.rs, which perhaps fix your error for now. Like this,

  fn oneof(&self, _oneof: &OneofDescriptor) -> Customize {
    Customize::default()
      .before("#[derive(::serde::Serialize, ::serde::Deserialize)]\n#[serde(untagged)]")
  }

But over that, I also get errors on Serialize, Deserialize not implemented for EnumOrUnknown<_>. I cannot add things like serde attribute macro for enum variant or whatever. E.g:

 #[derive(Clone,PartialEq,Debug)]
    #[non_exhaustive]
    #[derive(::serde::Serialize, ::serde::Deserialize)]
    #[serde(untagged)]
    // @@protoc_insertion_point(oneof:ShapeType.type)
    pub enum Type {
        // @@protoc_insertion_point(oneof_field:ShapeType.Rectangle)
        Rectangle(f64),
        // @@protoc_insertion_point(oneof_field:ShapeType.Square)
        Square(f64),
        // @@protoc_insertion_point(oneof_field:ShapeType.Circle)
        Circle(f64),
        // @@protoc_insertion_point(oneof_field:ShapeType.wall)
        Wall(::protobuf::EnumOrUnknown<super::Wall>),
    }

    impl ::protobuf::Oneof for Type {
    }

    impl ::protobuf::OneofFull for Type {
        fn descriptor() -> ::protobuf::reflect::OneofDescriptor {
            static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::OneofDescriptor> = ::protobuf::rt::Lazy::new();
            descriptor.get(|| <super::ShapeType as ::protobuf::MessageFull>::descriptor().oneof_by_name("type").unwrap()).clone()
        }
    }

    impl Type {
        pub(in super) fn generated_oneof_descriptor_data() -> ::protobuf::reflect::GeneratedOneofDescriptorData {
            ::protobuf::reflect::GeneratedOneofDescriptorData::new::<Type>("type")
        }
    }

Errors:

1264 |     #[derive(::serde::Serialize, ::serde::Deserialize)]
     |              ^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `EnumOrUnknown<Wall>`
...
1275 |         Wall(::protobuf::EnumOrUnknown<super::Wall>),
     |              - required by a bound introduced by this call

1275 |         Wall(::protobuf::EnumOrUnknown<super::Wall>),
     |              ^ the trait `parse_proto::_::_serde::Deserialize<'_>` is not implemented for `EnumOrUnknown<Wall>`

zrus avatar Jun 03 '23 09:06 zrus