From 2aa859da4b6539c0d7518a7647c1b9705720db91 Mon Sep 17 00:00:00 2001 From: Casper Meijn Date: Tue, 10 Sep 2024 17:12:17 +0200 Subject: [PATCH] feat(prost-types): derive Arbitrary Add feature to derive `trait Arbitrary` for each type. --- .github/workflows/ci.yml | 2 +- prost-types/Cargo.toml | 2 ++ prost-types/src/compiler.rs | 5 ++++ prost-types/src/lib.rs | 1 + prost-types/src/protobuf.rs | 54 ++++++++++++++++++++++++++++++++++++ prost-types/src/timestamp.rs | 16 +++++++++++ tests/src/bootstrap.rs | 4 +++ 7 files changed, 83 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72c8b6fe7..069ff81e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,7 +148,7 @@ jobs: uses: ./.github/actions/setup-ninja - uses: Swatinem/rust-cache@v2 - name: test - run: cargo test + run: cargo test --features arbitrary - name: test no-default-features run: cargo test -p prost-build -p prost-derive -p prost-types --no-default-features diff --git a/prost-types/Cargo.toml b/prost-types/Cargo.toml index 3d61d367e..5fede2632 100644 --- a/prost-types/Cargo.toml +++ b/prost-types/Cargo.toml @@ -15,9 +15,11 @@ doctest = false [features] default = ["std"] std = ["prost/std"] +arbitrary = ["dep:arbitrary"] [dependencies] prost = { version = "0.13.4", path = "../prost", default-features = false, features = ["prost-derive"] } +arbitrary = { version = "1.4", features = ["derive"], optional = true } [dev-dependencies] proptest = "1" diff --git a/prost-types/src/compiler.rs b/prost-types/src/compiler.rs index 862a2a532..d274aeba8 100644 --- a/prost-types/src/compiler.rs +++ b/prost-types/src/compiler.rs @@ -1,5 +1,6 @@ // This file is @generated by prost-build. /// The version number of protocol compiler. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Version { #[prost(int32, optional, tag = "1")] @@ -14,6 +15,7 @@ pub struct Version { pub suffix: ::core::option::Option<::prost::alloc::string::String>, } /// An encoded CodeGeneratorRequest is written to the plugin's stdin. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CodeGeneratorRequest { /// The .proto files that were explicitly listed on the command-line. The @@ -45,6 +47,7 @@ pub struct CodeGeneratorRequest { pub compiler_version: ::core::option::Option, } /// The plugin writes an encoded CodeGeneratorResponse to stdout. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CodeGeneratorResponse { /// Error message. If non-empty, code generation failed. The plugin process @@ -67,6 +70,7 @@ pub struct CodeGeneratorResponse { /// Nested message and enum types in `CodeGeneratorResponse`. pub mod code_generator_response { /// Represents a single generated file. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct File { /// The file name, relative to the output directory. The name must not @@ -131,6 +135,7 @@ pub mod code_generator_response { pub generated_code_info: ::core::option::Option, } /// Sync with code_generator.h. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, diff --git a/prost-types/src/lib.rs b/prost-types/src/lib.rs index 8f65fb6de..9cd5ed0f3 100644 --- a/prost-types/src/lib.rs +++ b/prost-types/src/lib.rs @@ -35,6 +35,7 @@ //! //! ## Feature Flags //! - `std`: Enable integration with standard library. Disable this feature for `no_std` support. This feature is enabled by default. +//! - `arbitrary`: Enable integration with crate `arbitrary`. All types on this crate will implement `trait Arbitrary`. //! //! [1]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf diff --git a/prost-types/src/protobuf.rs b/prost-types/src/protobuf.rs index 6f75dfc2b..b24264901 100644 --- a/prost-types/src/protobuf.rs +++ b/prost-types/src/protobuf.rs @@ -1,12 +1,14 @@ // This file is @generated by prost-build. /// The protocol compiler can output a FileDescriptorSet containing the .proto /// files it parses. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FileDescriptorSet { #[prost(message, repeated, tag = "1")] pub file: ::prost::alloc::vec::Vec, } /// Describes a complete .proto file. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FileDescriptorProto { /// file name, relative to root of source tree @@ -48,6 +50,7 @@ pub struct FileDescriptorProto { pub syntax: ::core::option::Option<::prost::alloc::string::String>, } /// Describes a message type. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct DescriptorProto { #[prost(string, optional, tag = "1")] @@ -75,6 +78,7 @@ pub struct DescriptorProto { } /// Nested message and enum types in `DescriptorProto`. pub mod descriptor_proto { + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExtensionRange { /// Inclusive. @@ -89,6 +93,7 @@ pub mod descriptor_proto { /// Range of reserved tag numbers. Reserved tag numbers may not be used by /// fields or extension ranges in the same message. Reserved ranges may /// not overlap. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct ReservedRange { /// Inclusive. @@ -99,6 +104,7 @@ pub mod descriptor_proto { pub end: ::core::option::Option, } } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExtensionRangeOptions { /// The parser stores options it doesn't recognize here. See above. @@ -106,6 +112,7 @@ pub struct ExtensionRangeOptions { pub uninterpreted_option: ::prost::alloc::vec::Vec, } /// Describes a field within a message. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FieldDescriptorProto { #[prost(string, optional, tag = "1")] @@ -174,6 +181,7 @@ pub struct FieldDescriptorProto { } /// Nested message and enum types in `FieldDescriptorProto`. pub mod field_descriptor_proto { + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -272,6 +280,7 @@ pub mod field_descriptor_proto { } } } + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -314,6 +323,7 @@ pub mod field_descriptor_proto { } } /// Describes a oneof. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OneofDescriptorProto { #[prost(string, optional, tag = "1")] @@ -322,6 +332,7 @@ pub struct OneofDescriptorProto { pub options: ::core::option::Option, } /// Describes an enum type. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumDescriptorProto { #[prost(string, optional, tag = "1")] @@ -350,6 +361,7 @@ pub mod enum_descriptor_proto { /// Note that this is distinct from DescriptorProto.ReservedRange in that it /// is inclusive such that it can appropriately represent the entire int32 /// domain. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct EnumReservedRange { /// Inclusive. @@ -361,6 +373,7 @@ pub mod enum_descriptor_proto { } } /// Describes a value within an enum. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumValueDescriptorProto { #[prost(string, optional, tag = "1")] @@ -371,6 +384,7 @@ pub struct EnumValueDescriptorProto { pub options: ::core::option::Option, } /// Describes a service. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ServiceDescriptorProto { #[prost(string, optional, tag = "1")] @@ -381,6 +395,7 @@ pub struct ServiceDescriptorProto { pub options: ::core::option::Option, } /// Describes a method of a service. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MethodDescriptorProto { #[prost(string, optional, tag = "1")] @@ -429,6 +444,7 @@ pub struct MethodDescriptorProto { /// /// If this turns out to be popular, a web service will be set up /// to automatically assign option numbers. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FileOptions { /// Sets the Java package where classes generated from this .proto will be @@ -547,6 +563,7 @@ pub struct FileOptions { /// Nested message and enum types in `FileOptions`. pub mod file_options { /// Generated classes can be optimized for speed or code size. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -592,6 +609,7 @@ pub mod file_options { } } } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MessageOptions { /// Set true to use the old proto1 MessageSet wire format for extensions. @@ -652,6 +670,7 @@ pub struct MessageOptions { #[prost(message, repeated, tag = "999")] pub uninterpreted_option: ::prost::alloc::vec::Vec, } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FieldOptions { /// The ctype option instructs the C++ code generator to use a different @@ -734,6 +753,7 @@ pub struct FieldOptions { } /// Nested message and enum types in `FieldOptions`. pub mod field_options { + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -774,6 +794,7 @@ pub mod field_options { } } } + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -817,12 +838,14 @@ pub mod field_options { } } } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OneofOptions { /// The parser stores options it doesn't recognize here. See above. #[prost(message, repeated, tag = "999")] pub uninterpreted_option: ::prost::alloc::vec::Vec, } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumOptions { /// Set this option to true to allow mapping different tag names to the same @@ -839,6 +862,7 @@ pub struct EnumOptions { #[prost(message, repeated, tag = "999")] pub uninterpreted_option: ::prost::alloc::vec::Vec, } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumValueOptions { /// Is this enum value deprecated? @@ -851,6 +875,7 @@ pub struct EnumValueOptions { #[prost(message, repeated, tag = "999")] pub uninterpreted_option: ::prost::alloc::vec::Vec, } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ServiceOptions { /// Is this service deprecated? @@ -863,6 +888,7 @@ pub struct ServiceOptions { #[prost(message, repeated, tag = "999")] pub uninterpreted_option: ::prost::alloc::vec::Vec, } +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MethodOptions { /// Is this method deprecated? @@ -887,6 +913,7 @@ pub mod method_options { /// Is this method side-effect-free (or safe in HTTP parlance), or idempotent, /// or neither? HTTP based RPC implementation may choose GET verb for safe /// methods, and PUT verb for idempotent methods instead of the default POST. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -935,6 +962,7 @@ pub mod method_options { /// options protos in descriptor objects (e.g. returned by Descriptor::options(), /// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions /// in them. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct UninterpretedOption { #[prost(message, repeated, tag = "2")] @@ -961,6 +989,7 @@ pub mod uninterpreted_option { /// extension (denoted with parentheses in options specs in .proto files). /// E.g.,{ \["foo", false\], \["bar.baz", true\], \["qux", false\] } represents /// "foo.(bar.baz).qux". + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NamePart { #[prost(string, required, tag = "1")] @@ -971,6 +1000,7 @@ pub mod uninterpreted_option { } /// Encapsulates information about the original source file from which a /// FileDescriptorProto was generated. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SourceCodeInfo { /// A Location identifies a piece of source code in a .proto file which @@ -1022,6 +1052,7 @@ pub struct SourceCodeInfo { } /// Nested message and enum types in `SourceCodeInfo`. pub mod source_code_info { + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Location { /// Identifies which part of the FileDescriptorProto was defined at this @@ -1116,6 +1147,7 @@ pub mod source_code_info { /// Describes the relationship between generated code and its original source /// file. A GeneratedCodeInfo message is associated with only one generated /// source file, but may contain references to different source .proto files. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GeneratedCodeInfo { /// An Annotation connects some span of text in generated code to an element @@ -1125,6 +1157,7 @@ pub struct GeneratedCodeInfo { } /// Nested message and enum types in `GeneratedCodeInfo`. pub mod generated_code_info { + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Annotation { /// Identifies the element in the original source .proto file. This field @@ -1238,6 +1271,7 @@ pub mod generated_code_info { /// "value": "1.212s" /// } /// ``` +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Any { /// A URL/resource name that uniquely identifies the type of the serialized @@ -1275,6 +1309,7 @@ pub struct Any { } /// `SourceContext` represents information about the source of a /// protobuf element, like the file in which it is defined. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SourceContext { /// The path-qualified name of the .proto file that contained the associated @@ -1283,6 +1318,7 @@ pub struct SourceContext { pub file_name: ::prost::alloc::string::String, } /// A protocol buffer message type. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Type { /// The fully qualified message name. @@ -1305,6 +1341,7 @@ pub struct Type { pub syntax: i32, } /// A single field of a message type. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Field { /// The field type. @@ -1343,6 +1380,7 @@ pub struct Field { /// Nested message and enum types in `Field`. pub mod field { /// Basic field types. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -1450,6 +1488,7 @@ pub mod field { } } /// Whether a field is optional, required, or repeated. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Clone, Copy, @@ -1498,6 +1537,7 @@ pub mod field { } } /// Enum type definition. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Enum { /// Enum type name. @@ -1517,6 +1557,7 @@ pub struct Enum { pub syntax: i32, } /// Enum value definition. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnumValue { /// Enum value name. @@ -1531,6 +1572,7 @@ pub struct EnumValue { } /// A protocol buffer option, which can be attached to a message, field, /// enumeration, etc. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Option { /// The option's name. For protobuf built-in options (options defined in @@ -1547,6 +1589,7 @@ pub struct Option { pub value: ::core::option::Option, } /// The syntax in which a protocol buffer element is defined. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum Syntax { @@ -1584,6 +1627,7 @@ impl Syntax { /// sometimes simply referred to as "APIs" in other contexts, such as the name of /// this message itself. See for /// detailed terminology. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Api { /// The fully qualified name of this interface, including package name @@ -1629,6 +1673,7 @@ pub struct Api { pub syntax: i32, } /// Method represents a method of an API interface. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Method { /// The simple name of this method. @@ -1741,6 +1786,7 @@ pub struct Method { /// ... /// } /// ``` +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Mixin { /// The fully qualified name of the interface which is included. @@ -1815,6 +1861,7 @@ pub struct Mixin { /// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should /// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 /// microsecond should be expressed in JSON format as "3.000001s". +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct Duration { /// Signed seconds of the span of time. Must be from -315,576,000,000 @@ -2053,6 +2100,7 @@ pub struct Duration { /// The implementation of any API method which has a FieldMask type field in the /// request should verify the included field paths, and return an /// `INVALID_ARGUMENT` error if any path is unmappable. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FieldMask { /// The set of field mask paths. @@ -2067,6 +2115,7 @@ pub struct FieldMask { /// with the proto support for the language. /// /// The JSON representation for `Struct` is JSON object. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Struct { /// Unordered map of dynamically typed values. @@ -2082,6 +2131,7 @@ pub struct Struct { /// variants. Absence of any variant indicates an error. /// /// The JSON representation for `Value` is JSON value. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Value { /// The kind of value. @@ -2091,6 +2141,7 @@ pub struct Value { /// Nested message and enum types in `Value`. pub mod value { /// The kind of value. + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Kind { /// Represents a null value. @@ -2116,6 +2167,7 @@ pub mod value { /// `ListValue` is a wrapper around a repeated field of values. /// /// The JSON representation for `ListValue` is JSON array. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListValue { /// Repeated field of dynamically typed values. @@ -2126,6 +2178,7 @@ pub struct ListValue { /// `Value` type union. /// /// The JSON representation for `NullValue` is JSON `null`. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum NullValue { @@ -2249,6 +2302,7 @@ impl NullValue { /// [`strftime`]() with /// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use /// the Joda Time's [`ISODateTimeFormat.dateTime()`]() to obtain a formatter capable of generating timestamps in this format. +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct Timestamp { /// Represents seconds of UTC time since Unix epoch diff --git a/prost-types/src/timestamp.rs b/prost-types/src/timestamp.rs index 1d7e609f4..9ed0db6ea 100644 --- a/prost-types/src/timestamp.rs +++ b/prost-types/src/timestamp.rs @@ -426,4 +426,20 @@ mod tests { ); } } + + #[cfg(feature = "arbitrary")] + #[test] + fn check_timestamp_implements_arbitrary() { + use arbitrary::{Arbitrary, Unstructured}; + + let mut unstructured = Unstructured::new(&[]); + + assert_eq!( + Timestamp::arbitrary(&mut unstructured), + Ok(Timestamp { + seconds: 0, + nanos: 0 + }) + ); + } } diff --git a/tests/src/bootstrap.rs b/tests/src/bootstrap.rs index 28b3fa3b0..0068249e8 100644 --- a/tests/src/bootstrap.rs +++ b/tests/src/bootstrap.rs @@ -40,6 +40,10 @@ fn bootstrap() { prost_build::Config::new() .compile_well_known_types() .btree_map(["."]) + .type_attribute( + ".", + r#"#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]"#, + ) .out_dir(tempdir.path()) .compile_protos( &[