Description
I have a lot of protobuf messages like this:
message MyServiceResponse {
message Success {
// This is just one example, the Success message could in principle hold anything
oneof status {
Done done = 1;
Skipped skipped = 2;
}
}
oneof result {
Success success = 1;
InvalidRequest invalid_request = 2;
InternalError internal_error = 3;
NotFound not_found = 4;
}
}
message InvalidRequest {
string error = 1;
}
message InternalError {
string error = 2;
}
message NotFound {}
message Done {}
message Skipped {}
prost_build takes this and generates something like this:
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MyServiceResponse {
#[prost(oneof = "my_service_response::Result", tags = "1, 2, 3, 4")]
pub result: ::core::option::Option<my_service_response::Result>,
}
pub mod my_service_response {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Success {
#[prost(oneof = "success::Status", tags = "1, 2")]
pub status: ::core::option::Option<success::Status>,
}
pub mod success {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Status {
#[prost(message, tag = "1")]
Done(Done),
#[prost(message, tag = "2")]
Skipped(Skipped),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Success(Success),
#[prost(message, tag = "2")]
InvalidRequest(InvalidRequest),
#[prost(message, tag = "3")]
InternalError(InternalError),
#[prost(message, tag = "4")]
DocumentNotFound(NotFound),
}
}
Constructing an instance of MyServiceResponse
is unfortunately quite tedious:
let response = MyServiceResponse {
result: Some(my_service_response::Result::Success(
my_service_response::Success {
status: Some(my_service_response::success::Status::Done(
Done {},
)),
},
)),
};
That's quite a deep level of nesting where the only actual field comes at the end.
Therefore I've taken to writing methods like this:
fn success(status: my_service_response::success::Status) -> Self {
Self {
result: Some(my_service_response::Result::Success(
my_service_response::Success {
status: Some(status),
},
)),
};
}
Which lets me construct responses much more concisely. However, writing this function is still quite tedious, especially if there's a lot of messages like this.
At first, I thought I could fix this myself, but I don't see any way to generate these functions based only on the resulting Rust code (i.e. by writing a macro) as that would require knowledge of multiple types at once. At least it seems very difficult.
I'm wondering if prost-build
could somehow automatically generate constructors like this? I could imagine it would make construction of messages a lot easier, not just for this specific message but for messages in general. Basically the idea would be to generate this kind of function for messages that only have one field, and if that one also only has one field, then take the input as that field (and recursively etc.). Hope that makes sense.