@@ -244,6 +244,8 @@ pub struct Config {
244244 map_type : PathMap < MapType > ,
245245 bytes_type : PathMap < BytesType > ,
246246 type_attributes : PathMap < String > ,
247+ message_attributes : PathMap < String > ,
248+ enum_attributes : PathMap < String > ,
247249 field_attributes : PathMap < String > ,
248250 prost_types : bool ,
249251 strip_enum_prefix : bool ,
@@ -468,6 +470,94 @@ impl Config {
468470 self
469471 }
470472
473+ /// Add additional attribute to matched messages.
474+ ///
475+ /// # Arguments
476+ ///
477+ /// **`paths`** - a path matching any number of types. It works the same way as in
478+ /// [`btree_map`](#method.btree_map), just with the field name omitted.
479+ ///
480+ /// **`attribute`** - an arbitrary string to be placed before each matched type. The
481+ /// expected usage are additional attributes, but anything is allowed.
482+ ///
483+ /// The calls to this method are cumulative. They don't overwrite previous calls and if a
484+ /// type is matched by multiple calls of the method, all relevant attributes are added to
485+ /// it.
486+ ///
487+ /// For things like serde it might be needed to combine with [field
488+ /// attributes](#method.field_attribute).
489+ ///
490+ /// # Examples
491+ ///
492+ /// ```rust
493+ /// # let mut config = prost_build::Config::new();
494+ /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
495+ /// config.message_attribute(".", "#[derive(Eq)]");
496+ /// // Some messages want to be serializable with serde as well.
497+ /// config.message_attribute("my_messages.MyMessageType",
498+ /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
499+ /// config.message_attribute("my_messages.MyMessageType.MyNestedMessageType",
500+ /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
501+ /// ```
502+ pub fn message_attribute < P , A > ( & mut self , path : P , attribute : A ) -> & mut Self
503+ where
504+ P : AsRef < str > ,
505+ A : AsRef < str > ,
506+ {
507+ self . message_attributes
508+ . insert ( path. as_ref ( ) . to_string ( ) , attribute. as_ref ( ) . to_string ( ) ) ;
509+ self
510+ }
511+
512+ /// Add additional attribute to matched enums and one-ofs.
513+ ///
514+ /// # Arguments
515+ ///
516+ /// **`paths`** - a path matching any number of types. It works the same way as in
517+ /// [`btree_map`](#method.btree_map), just with the field name omitted.
518+ ///
519+ /// **`attribute`** - an arbitrary string to be placed before each matched type. The
520+ /// expected usage are additional attributes, but anything is allowed.
521+ ///
522+ /// The calls to this method are cumulative. They don't overwrite previous calls and if a
523+ /// type is matched by multiple calls of the method, all relevant attributes are added to
524+ /// it.
525+ ///
526+ /// For things like serde it might be needed to combine with [field
527+ /// attributes](#method.field_attribute).
528+ ///
529+ /// # Examples
530+ ///
531+ /// ```rust
532+ /// # let mut config = prost_build::Config::new();
533+ /// // Nothing around uses floats, so we can derive real `Eq` in addition to `PartialEq`.
534+ /// config.enum_attribute(".", "#[derive(Eq)]");
535+ /// // Some messages want to be serializable with serde as well.
536+ /// config.enum_attribute("my_messages.MyEnumType",
537+ /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
538+ /// config.enum_attribute("my_messages.MyMessageType.MyNestedEnumType",
539+ /// "#[derive(Serialize)] #[serde(rename_all = \"snake_case\")]");
540+ /// ```
541+ ///
542+ /// # Oneof fields
543+ ///
544+ /// The `oneof` fields don't have a type name of their own inside Protobuf. Therefore, the
545+ /// field name can be used both with `enum_attribute` and `field_attribute` ‒ the first is
546+ /// placed before the `enum` type definition, the other before the field inside corresponding
547+ /// message `struct`.
548+ ///
549+ /// In other words, to place an attribute on the `enum` implementing the `oneof`, the match
550+ /// would look like `my_messages.MyNestedMessageType.oneofname`.
551+ pub fn enum_attribute < P , A > ( & mut self , path : P , attribute : A ) -> & mut Self
552+ where
553+ P : AsRef < str > ,
554+ A : AsRef < str > ,
555+ {
556+ self . enum_attributes
557+ . insert ( path. as_ref ( ) . to_string ( ) , attribute. as_ref ( ) . to_string ( ) ) ;
558+ self
559+ }
560+
471561 /// Configures the code generator to use the provided service generator.
472562 pub fn service_generator ( & mut self , service_generator : Box < dyn ServiceGenerator > ) -> & mut Self {
473563 self . service_generator = Some ( service_generator) ;
@@ -1099,6 +1189,8 @@ impl default::Default for Config {
10991189 map_type : PathMap :: default ( ) ,
11001190 bytes_type : PathMap :: default ( ) ,
11011191 type_attributes : PathMap :: default ( ) ,
1192+ message_attributes : PathMap :: default ( ) ,
1193+ enum_attributes : PathMap :: default ( ) ,
11021194 field_attributes : PathMap :: default ( ) ,
11031195 prost_types : true ,
11041196 strip_enum_prefix : true ,
@@ -1425,6 +1517,37 @@ mod tests {
14251517 assert_eq ! ( state. finalized, 3 ) ;
14261518 }
14271519
1520+ #[ test]
1521+ fn test_generate_message_attributes ( ) {
1522+ let _ = env_logger:: try_init ( ) ;
1523+
1524+ let out_dir = std:: env:: temp_dir ( ) ;
1525+
1526+ Config :: new ( )
1527+ . out_dir ( out_dir. clone ( ) )
1528+ . message_attribute ( "." , "#[derive(derive_builder::Builder)]" )
1529+ . enum_attribute ( "." , "#[some_enum_attr(u8)]" )
1530+ . compile_protos (
1531+ & [ "src/fixtures/helloworld/hello.proto" ] ,
1532+ & [ "src/fixtures/helloworld" ] ,
1533+ )
1534+ . unwrap ( ) ;
1535+
1536+ let out_file = out_dir
1537+ . join ( "helloworld.rs" )
1538+ . as_path ( )
1539+ . display ( )
1540+ . to_string ( ) ;
1541+ let expected_content = read_all_content ( "src/fixtures/helloworld/_expected_helloworld.rs" )
1542+ . replace ( "\r \n " , "\n " ) ;
1543+ let content = read_all_content ( & out_file) . replace ( "\r \n " , "\n " ) ;
1544+ assert_eq ! (
1545+ expected_content, content,
1546+ "Unexpected content: \n {}" ,
1547+ content
1548+ ) ;
1549+ }
1550+
14281551 #[ test]
14291552 fn test_generate_no_empty_outputs ( ) {
14301553 let _ = env_logger:: try_init ( ) ;
0 commit comments