use proto_build_kit::{Stager, compile_protos, extract_method_string_extension};
const CONVENTIONS_PROTO: &[u8] = br#"
syntax = "proto3";
package my.opts;
import "google/protobuf/descriptor.proto";
extend google.protobuf.MethodOptions {
// Marks which proto field on the RESPONSE carries an ETag value
// suitable for optimistic concurrency / cache validation.
optional string etag_field = 90001;
}
"#;
const SERVICE_PROTO: &[u8] = br#"
syntax = "proto3";
package my.v1;
import "my/opts/conventions.proto";
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (my.opts.etag_field) = "version";
}
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
message GetUserRequest { string id = 1; }
message User {
string id = 1;
string name = 2;
uint64 version = 3;
}
message ListUsersRequest { uint32 page_size = 1; }
message ListUsersResponse { repeated User items = 1; }
"#;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let staged = Stager::new()
.add("my/opts/conventions.proto", CONVENTIONS_PROTO)
.add("my/v1/service.proto", SERVICE_PROTO)
.stage()?;
let out = compile_protos(
&["my/v1/service.proto", "my/opts/conventions.proto"],
&[staged.path()],
)?;
let etag_fields = extract_method_string_extension(&out.pool, "my.opts.etag_field");
println!("Methods declaring (my.opts.etag_field):");
if etag_fields.is_empty() {
println!(" (none)");
} else {
for (response_fqn, field_name) in &etag_fields {
println!(" {response_fqn} → response field `{field_name}`");
}
}
Ok(())
}