prost
                                
                                 prost copied to clipboard
                                
                                    prost copied to clipboard
                            
                            
                            
                        How to access EnumValueOption
I'm struggling to figure out how to access EnumValueOption extensions on an enum. For example, given
proto/demo.proto:
syntax = "proto3";
import "google/protobuf/descriptor.proto";
package demo;
extend google.protobuf.EnumValueOptions {
  optional uint32 len = 50000;
}
enum Foo {
  None = 0 [(len) = 0];
  One = 1 [(len) = 1];
  Two = 2 [(len) = 2];
}
and
build.rs:
use std::path::PathBuf;
fn main() {
    let out_dir =
        PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR environment variable not set."));
    prost_build::Config::new()
        .file_descriptor_set_path(out_dir.join("file_descriptor_set.bin"))
        .compile_protos(&["proto/demo.proto"], &["proto"])
        .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}
I suspect I can somehow access the len options using the FileDescriptorSet, but I can't figure out how.
src/lib.rs:
use prost::Message;
use prost_types::FileDescriptorSet;
include!(concat!(env!("OUT_DIR"), concat!("/demo.rs")));
pub fn parse_file_descriptor_set() -> FileDescriptorSet {
    let file_descriptor_set_bytes =
        include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
    prost_types::FileDescriptorSet::decode(&file_descriptor_set_bytes[..]).unwrap()
}
#[cfg(test)]
mod tests {
    use crate::parse_file_descriptor_set;
    #[test]
    fn get_len() {
        let set = parse_file_descriptor_set();
        for f in &set.file {
            dbg!(&f.extension);
        }
        todo!()
    }
}
@shradej1 were you able to figure this out?
Nope, still hoping for a response.
I think the right place to look also would be in protoc what it generates since FileDescriptorSet etc is all just generated from protoc I don't know off the top of my head what gets generated into there beyond what we use.
I wonder if this is just missing functionality.  If I change get_len() as follows
fn get_len(foo: Foo) -> u32 {
        let set = parse_file_descriptor_set();
        for f in &set.file {
            for ext in &f.extension {
                dbg!(ext);
            }
            for e in &f.enum_type {
                for v in &e.value {
                    dbg!(&v.options);
                }
            }
        }
        todo!()
    }
I see
[src/lib.rs:22] ext = FieldDescriptorProto {
    name: Some(
        "len",
    ),
    number: Some(
        50000,
    ),
    label: Some(
        Optional,
    ),
    r#type: Some(
        Uint32,
    ),
    type_name: None,
    extendee: Some(
        ".google.protobuf.EnumValueOptions",
    ),
    default_value: None,
    oneof_index: None,
    json_name: Some(
        "len",
    ),
    options: None,
    proto3_optional: Some(
        true,
    ),
}
so the extension is being parsed.  I don't see the actual len values being stored anywhere though.  However, if I add a deprecated option to one of the enum values - None = 0 [(len) = 10, deprecated = true];, it does show up.
[src/lib.rs:29] &v.options = Some(
    EnumValueOptions {
        deprecated: Some(
            true,
        ),
        uninterpreted_option: [],
    },
)
I feel like len should be captured in that uninterpreted_option, which I should be able to parse via (missing?) functionality in the FieldDescriptorProto extension?  I might be able to take a peek and figure out where deprecated is being set, and what would actually end up in the uninterpreted_option of EnumValueOption.
I may be missing something. I think that extension value is actually encoded in the serialized message, in which case I'd need the byte stream - not just the enum value - to retrieve it.
I feel like len should be captured in that uninterpreted_option, which I should be able to parse via (missing?) functionality in the FieldDescriptorProto extension? I might be able to take a peek and figure out where deprecated is being set, and what would actually end up in the uninterpreted_option of EnumValueOption.
It looks extension options are actually serialized as unknown fields, which prost doesn't currently capture (#574 might change that)
For example, the text format of your example file descriptor is:
file {
  name: "foo.proto"
  package: "demo"
  dependency: "google/protobuf/descriptor.proto"
  enum_type {
    name: "Foo"
    value {
      name: "None"
      number: 0
      options {
        50000: 0
      }
    }
    value {
      name: "One"
      number: 1
      options {
        50000: 1
      }
    }
    value {
      name: "Two"
      number: 2
      options {
        50000: 2
      }
    }
  }
  extension {
    name: "len"
    extendee: ".google.protobuf.EnumValueOptions"
    number: 50000
    label: LABEL_OPTIONAL
    type: TYPE_UINT32
    json_name: "len"
    proto3_optional: true
  }
  syntax: "proto3"
}
Which does technically contain enough information to reconstruct the option, but you have to inspect the extension fields as well.
My crate prost-reflect is able to retrieve the option values, for example:
let file_descriptor_set_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
let descriptors = prost_reflect::DescriptorPool::decode(file_descriptor_set_bytes.as_ref()).unwrap();
let file_descriptor_set = descriptors
    .get_message_by_name("google.protobuf.FileDescriptorSet")
    .unwrap();
let mut dynamic_message = prost_reflect::DynamicMessage::new(file_descriptor_set);
dynamic_message.merge(bytes.as_slice()).unwrap();
let extension = descriptors
    .get_message_by_name("google.protobuf.EnumValueOptions")
    .unwrap()
    .extensions()
    .find(|ext| ext.name() == "len")
    .unwrap();
assert_eq!(dynamic_message
    .get_field_by_name("file")
    .unwrap()
    .as_list()
    .unwrap()[1]
    .as_message()
    .unwrap()
    .get_field_by_name("enum_type")
    .unwrap()
    .as_list()
    .unwrap()[0]
    .as_message()
    .unwrap()
    .get_field_by_name("value")
    .unwrap()
    .as_list()
    .unwrap()[1]
    .as_message()
    .unwrap()
    .get_field_by_name("options")
    .unwrap()
    .as_message()
    .unwrap()
    .get_extension(&extension)
    .as_ref(), &Value::U32(1));
Ideally there would be solution to bind into a strongly-typed message to avoid the nasty dynamic message inspection, but I'm not aware of any crates that provide that