378 lines
13 KiB
Rust
378 lines
13 KiB
Rust
|
|
use std::collections::HashSet;
|
||
|
|
|
||
|
|
use file_descriptor::file_descriptor_proto_expr;
|
||
|
|
use inside::protobuf_crate_path;
|
||
|
|
use protobuf::descriptor::*;
|
||
|
|
use protobuf_name::ProtobufAbsolutePath;
|
||
|
|
use rust_types_values::type_name_to_rust_relative;
|
||
|
|
use scope::EnumWithScope;
|
||
|
|
use scope::RootScope;
|
||
|
|
use scope::WithScope;
|
||
|
|
use serde;
|
||
|
|
use CodeWriter;
|
||
|
|
|
||
|
|
use crate::customize::customize_from_rustproto_for_enum;
|
||
|
|
use crate::Customize;
|
||
|
|
|
||
|
|
#[derive(Clone)]
|
||
|
|
pub struct EnumValueGen {
|
||
|
|
proto: EnumValueDescriptorProto,
|
||
|
|
enum_rust_name: String,
|
||
|
|
variant_rust_name: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl EnumValueGen {
|
||
|
|
fn parse(
|
||
|
|
proto: &EnumValueDescriptorProto,
|
||
|
|
enum_rust_name: &str,
|
||
|
|
variant_rust_name: &str,
|
||
|
|
) -> EnumValueGen {
|
||
|
|
EnumValueGen {
|
||
|
|
proto: proto.clone(),
|
||
|
|
enum_rust_name: enum_rust_name.to_string(),
|
||
|
|
variant_rust_name: variant_rust_name.to_string(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// enum value
|
||
|
|
fn number(&self) -> i32 {
|
||
|
|
self.proto.get_number()
|
||
|
|
}
|
||
|
|
|
||
|
|
// name of enum variant in generated rust code
|
||
|
|
fn rust_name_inner(&self) -> String {
|
||
|
|
self.variant_rust_name.clone()
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn rust_name_outer(&self) -> String {
|
||
|
|
let mut r = String::new();
|
||
|
|
r.push_str(&self.enum_rust_name);
|
||
|
|
r.push_str("::");
|
||
|
|
r.push_str(&self.rust_name_inner());
|
||
|
|
r
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub(crate) struct EnumGen<'a> {
|
||
|
|
enum_with_scope: &'a EnumWithScope<'a>,
|
||
|
|
type_name: String,
|
||
|
|
lite_runtime: bool,
|
||
|
|
customize: Customize,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a> EnumGen<'a> {
|
||
|
|
pub fn new(
|
||
|
|
enum_with_scope: &'a EnumWithScope<'a>,
|
||
|
|
current_file: &FileDescriptorProto,
|
||
|
|
customize: &Customize,
|
||
|
|
root_scope: &RootScope,
|
||
|
|
) -> EnumGen<'a> {
|
||
|
|
let rust_name = if enum_with_scope.get_scope().get_file_descriptor().get_name()
|
||
|
|
== current_file.get_name()
|
||
|
|
{
|
||
|
|
// field type is a message or enum declared in the same file
|
||
|
|
enum_with_scope.rust_name().to_string()
|
||
|
|
} else {
|
||
|
|
type_name_to_rust_relative(
|
||
|
|
&ProtobufAbsolutePath::from(enum_with_scope.name_absolute()),
|
||
|
|
current_file,
|
||
|
|
false,
|
||
|
|
root_scope,
|
||
|
|
customize,
|
||
|
|
)
|
||
|
|
.to_string()
|
||
|
|
};
|
||
|
|
let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
|
||
|
|
enum_with_scope
|
||
|
|
.get_scope()
|
||
|
|
.get_file_descriptor()
|
||
|
|
.get_options()
|
||
|
|
.get_optimize_for()
|
||
|
|
== FileOptions_OptimizeMode::LITE_RUNTIME
|
||
|
|
});
|
||
|
|
|
||
|
|
let mut customize = customize.clone();
|
||
|
|
customize.update_with(&customize_from_rustproto_for_enum(
|
||
|
|
enum_with_scope.en.options.as_ref().unwrap_or_default(),
|
||
|
|
));
|
||
|
|
|
||
|
|
EnumGen {
|
||
|
|
enum_with_scope,
|
||
|
|
type_name: rust_name,
|
||
|
|
lite_runtime,
|
||
|
|
customize,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn allow_alias(&self) -> bool {
|
||
|
|
self.enum_with_scope.en.get_options().get_allow_alias()
|
||
|
|
}
|
||
|
|
|
||
|
|
fn values_all(&self) -> Vec<EnumValueGen> {
|
||
|
|
let mut r = Vec::new();
|
||
|
|
for p in self.enum_with_scope.values() {
|
||
|
|
r.push(EnumValueGen::parse(
|
||
|
|
&p.proto,
|
||
|
|
&self.type_name,
|
||
|
|
p.rust_name().get(),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
r
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn values_unique(&self) -> Vec<EnumValueGen> {
|
||
|
|
let mut used = HashSet::new();
|
||
|
|
let mut r = Vec::new();
|
||
|
|
for p in self.enum_with_scope.values() {
|
||
|
|
// skipping non-unique enums
|
||
|
|
// TODO: should support it
|
||
|
|
if !used.insert(p.proto.get_number()) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
r.push(EnumValueGen::parse(
|
||
|
|
p.proto,
|
||
|
|
&self.type_name,
|
||
|
|
p.rust_name().get(),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
r
|
||
|
|
}
|
||
|
|
|
||
|
|
// find enum value by name
|
||
|
|
pub fn value_by_name(&'a self, name: &str) -> EnumValueGen {
|
||
|
|
let v = self.enum_with_scope.value_by_name(name);
|
||
|
|
EnumValueGen::parse(v.proto, &self.type_name, v.rust_name().get())
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn write(&self, w: &mut CodeWriter) {
|
||
|
|
self.write_struct(w);
|
||
|
|
if self.allow_alias() {
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_eq(w);
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_hash(w);
|
||
|
|
}
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_enum(w);
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_copy(w);
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_default(w);
|
||
|
|
w.write_line("");
|
||
|
|
self.write_impl_value(w);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_struct(&self, w: &mut CodeWriter) {
|
||
|
|
let mut derive = Vec::new();
|
||
|
|
derive.push("Clone");
|
||
|
|
if !self.allow_alias() {
|
||
|
|
derive.push("PartialEq");
|
||
|
|
}
|
||
|
|
derive.push("Eq");
|
||
|
|
derive.push("Debug");
|
||
|
|
if !self.allow_alias() {
|
||
|
|
derive.push("Hash");
|
||
|
|
} else {
|
||
|
|
w.comment("Note: you cannot use pattern matching for enums with allow_alias option");
|
||
|
|
}
|
||
|
|
w.derive(&derive);
|
||
|
|
serde::write_serde_attr(
|
||
|
|
w,
|
||
|
|
&self.customize,
|
||
|
|
"derive(::serde::Serialize, ::serde::Deserialize)",
|
||
|
|
);
|
||
|
|
if let Some(ref ren) = self.customize.serde_rename_all {
|
||
|
|
let attr = format!("serde(rename_all = \"{}\")", ren);
|
||
|
|
serde::write_serde_attr(w, &self.customize, &attr);
|
||
|
|
}
|
||
|
|
let ref type_name = self.type_name;
|
||
|
|
w.expr_block(&format!("pub enum {}", type_name), |w| {
|
||
|
|
for value in self.values_all() {
|
||
|
|
if self.allow_alias() {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{}, // {}",
|
||
|
|
value.rust_name_inner(),
|
||
|
|
value.number()
|
||
|
|
));
|
||
|
|
} else {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{} = {},",
|
||
|
|
value.rust_name_inner(),
|
||
|
|
value.number()
|
||
|
|
));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_fn_value(&self, w: &mut CodeWriter) {
|
||
|
|
w.def_fn("value(&self) -> i32", |w| {
|
||
|
|
if self.allow_alias() {
|
||
|
|
w.match_expr("*self", |w| {
|
||
|
|
for value in self.values_all() {
|
||
|
|
w.case_expr(value.rust_name_outer(), format!("{}", value.number()));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
w.write_line("*self as i32")
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_enum(&self, w: &mut CodeWriter) {
|
||
|
|
let ref type_name = self.type_name;
|
||
|
|
w.impl_for_block(
|
||
|
|
&format!("{}::ProtobufEnum", protobuf_crate_path(&self.customize)),
|
||
|
|
&format!("{}", type_name),
|
||
|
|
|w| {
|
||
|
|
self.write_fn_value(w);
|
||
|
|
|
||
|
|
w.write_line("");
|
||
|
|
let ref type_name = self.type_name;
|
||
|
|
w.def_fn(
|
||
|
|
&format!(
|
||
|
|
"from_i32(value: i32) -> ::std::option::Option<{}>",
|
||
|
|
type_name
|
||
|
|
),
|
||
|
|
|w| {
|
||
|
|
w.match_expr("value", |w| {
|
||
|
|
let values = self.values_unique();
|
||
|
|
for value in values {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{} => ::std::option::Option::Some({}),",
|
||
|
|
value.number(),
|
||
|
|
value.rust_name_outer()
|
||
|
|
));
|
||
|
|
}
|
||
|
|
w.write_line(&format!("_ => ::std::option::Option::None"));
|
||
|
|
});
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
w.write_line("");
|
||
|
|
w.def_fn(&format!("values() -> &'static [Self]"), |w| {
|
||
|
|
w.write_line(&format!("static values: &'static [{}] = &[", type_name));
|
||
|
|
w.indented(|w| {
|
||
|
|
for value in self.values_all() {
|
||
|
|
w.write_line(&format!("{},", value.rust_name_outer()));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
w.write_line("];");
|
||
|
|
w.write_line("values");
|
||
|
|
});
|
||
|
|
|
||
|
|
if !self.lite_runtime {
|
||
|
|
w.write_line("");
|
||
|
|
w.def_fn(
|
||
|
|
&format!(
|
||
|
|
"enum_descriptor_static() -> &'static {}::reflect::EnumDescriptor",
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
),
|
||
|
|
|w| {
|
||
|
|
w.lazy_static_decl_get(
|
||
|
|
"descriptor",
|
||
|
|
&format!(
|
||
|
|
"{}::reflect::EnumDescriptor",
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
),
|
||
|
|
&self.customize,
|
||
|
|
|w| {
|
||
|
|
let ref type_name = self.type_name;
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{}::reflect::EnumDescriptor::new_pb_name::<{}>(\"{}\", {})",
|
||
|
|
protobuf_crate_path(&self.customize),
|
||
|
|
type_name,
|
||
|
|
self.enum_with_scope.name_to_package(),
|
||
|
|
file_descriptor_proto_expr(&self.enum_with_scope.scope)
|
||
|
|
));
|
||
|
|
},
|
||
|
|
);
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_value(&self, w: &mut CodeWriter) {
|
||
|
|
w.impl_for_block(
|
||
|
|
&format!(
|
||
|
|
"{}::reflect::ProtobufValue",
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
),
|
||
|
|
&format!("{}", self.type_name),
|
||
|
|
|w| {
|
||
|
|
w.def_fn(
|
||
|
|
&format!(
|
||
|
|
"as_ref(&self) -> {}::reflect::ReflectValueRef",
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
),
|
||
|
|
|w| {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{}::reflect::ReflectValueRef::Enum({}::ProtobufEnum::descriptor(self))",
|
||
|
|
protobuf_crate_path(&self.customize),
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
))
|
||
|
|
},
|
||
|
|
)
|
||
|
|
},
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_copy(&self, w: &mut CodeWriter) {
|
||
|
|
w.impl_for_block("::std::marker::Copy", &self.type_name, |_w| {});
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_eq(&self, w: &mut CodeWriter) {
|
||
|
|
assert!(self.allow_alias());
|
||
|
|
w.impl_for_block(
|
||
|
|
"::std::cmp::PartialEq",
|
||
|
|
&format!("{}", self.type_name),
|
||
|
|
|w| {
|
||
|
|
w.def_fn("eq(&self, other: &Self) -> bool", |w| {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{}::ProtobufEnum::value(self) == {}::ProtobufEnum::value(other)",
|
||
|
|
protobuf_crate_path(&self.customize),
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
));
|
||
|
|
});
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_hash(&self, w: &mut CodeWriter) {
|
||
|
|
assert!(self.allow_alias());
|
||
|
|
w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
|
||
|
|
w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"state.write_i32({}::ProtobufEnum::value(self))",
|
||
|
|
protobuf_crate_path(&self.customize)
|
||
|
|
));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_impl_default(&self, w: &mut CodeWriter) {
|
||
|
|
let first_value = &self.enum_with_scope.values()[0];
|
||
|
|
if first_value.proto.get_number() != 0 {
|
||
|
|
// This warning is emitted only for proto2
|
||
|
|
// (because in proto3 first enum variant number is always 0).
|
||
|
|
// `Default` implemented unconditionally to simplify certain
|
||
|
|
// generic operations, e. g. reading a map.
|
||
|
|
// Also, note that even in proto2 some operations fallback to
|
||
|
|
// first enum value, e. g. `get_xxx` for unset field,
|
||
|
|
// so this implementation is not completely unreasonable.
|
||
|
|
w.comment("Note, `Default` is implemented although default value is not 0");
|
||
|
|
}
|
||
|
|
w.impl_for_block("::std::default::Default", &self.type_name, |w| {
|
||
|
|
w.def_fn("default() -> Self", |w| {
|
||
|
|
w.write_line(&format!(
|
||
|
|
"{}::{}",
|
||
|
|
&self.type_name,
|
||
|
|
&first_value.rust_name()
|
||
|
|
))
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|