Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 1 | use std::collections::HashSet; |
| 2 | |
| 3 | use protobuf::descriptor::*; |
| 4 | |
| 5 | use super::code_writer::*; |
| 6 | use super::customize::Customize; |
| 7 | use file_descriptor::file_descriptor_proto_expr; |
Haibo Huang | 72fec01 | 2020-07-10 20:24:04 -0700 | [diff] [blame^] | 8 | use inside::protobuf_crate_path; |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 9 | use protobuf_name::ProtobufAbsolutePath; |
| 10 | use rust_types_values::type_name_to_rust_relative; |
| 11 | use scope::EnumWithScope; |
| 12 | use scope::RootScope; |
| 13 | use scope::WithScope; |
| 14 | use serde; |
| 15 | |
| 16 | #[derive(Clone)] |
| 17 | pub struct EnumValueGen { |
| 18 | proto: EnumValueDescriptorProto, |
| 19 | enum_rust_name: String, |
| 20 | variant_rust_name: String, |
| 21 | } |
| 22 | |
| 23 | impl EnumValueGen { |
| 24 | fn parse( |
| 25 | proto: &EnumValueDescriptorProto, |
| 26 | enum_rust_name: &str, |
| 27 | variant_rust_name: &str, |
| 28 | ) -> EnumValueGen { |
| 29 | EnumValueGen { |
| 30 | proto: proto.clone(), |
| 31 | enum_rust_name: enum_rust_name.to_string(), |
| 32 | variant_rust_name: variant_rust_name.to_string(), |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | // enum value |
| 37 | fn number(&self) -> i32 { |
| 38 | self.proto.get_number() |
| 39 | } |
| 40 | |
| 41 | // name of enum variant in generated rust code |
| 42 | fn rust_name_inner(&self) -> String { |
| 43 | self.variant_rust_name.clone() |
| 44 | } |
| 45 | |
| 46 | pub fn rust_name_outer(&self) -> String { |
| 47 | let mut r = String::new(); |
| 48 | r.push_str(&self.enum_rust_name); |
| 49 | r.push_str("::"); |
| 50 | r.push_str(&self.rust_name_inner()); |
| 51 | r |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | pub(crate) struct EnumGen<'a> { |
| 56 | enum_with_scope: &'a EnumWithScope<'a>, |
| 57 | type_name: String, |
| 58 | lite_runtime: bool, |
| 59 | customize: Customize, |
| 60 | } |
| 61 | |
| 62 | impl<'a> EnumGen<'a> { |
| 63 | pub fn new( |
| 64 | enum_with_scope: &'a EnumWithScope<'a>, |
| 65 | current_file: &FileDescriptorProto, |
| 66 | customize: &Customize, |
| 67 | root_scope: &RootScope, |
| 68 | ) -> EnumGen<'a> { |
| 69 | let rust_name = if enum_with_scope.get_scope().get_file_descriptor().get_name() |
| 70 | == current_file.get_name() |
| 71 | { |
| 72 | // field type is a message or enum declared in the same file |
| 73 | enum_with_scope.rust_name().to_string() |
| 74 | } else { |
| 75 | type_name_to_rust_relative( |
| 76 | &ProtobufAbsolutePath::from(enum_with_scope.name_absolute()), |
| 77 | current_file, |
| 78 | false, |
| 79 | root_scope, |
| 80 | ) |
| 81 | .to_string() |
| 82 | }; |
| 83 | let lite_runtime = customize.lite_runtime.unwrap_or_else(|| { |
| 84 | enum_with_scope |
| 85 | .get_scope() |
| 86 | .get_file_descriptor() |
| 87 | .get_options() |
| 88 | .get_optimize_for() |
| 89 | == FileOptions_OptimizeMode::LITE_RUNTIME |
| 90 | }); |
| 91 | |
| 92 | EnumGen { |
| 93 | enum_with_scope, |
| 94 | type_name: rust_name, |
| 95 | lite_runtime, |
| 96 | customize: customize.clone(), |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | fn allow_alias(&self) -> bool { |
| 101 | self.enum_with_scope.en.get_options().get_allow_alias() |
| 102 | } |
| 103 | |
| 104 | fn values_all(&self) -> Vec<EnumValueGen> { |
| 105 | let mut r = Vec::new(); |
| 106 | for p in self.enum_with_scope.values() { |
| 107 | r.push(EnumValueGen::parse( |
| 108 | &p.proto, |
| 109 | &self.type_name, |
| 110 | p.rust_name().get(), |
| 111 | )); |
| 112 | } |
| 113 | r |
| 114 | } |
| 115 | |
| 116 | pub fn values_unique(&self) -> Vec<EnumValueGen> { |
| 117 | let mut used = HashSet::new(); |
| 118 | let mut r = Vec::new(); |
| 119 | for p in self.enum_with_scope.values() { |
| 120 | // skipping non-unique enums |
| 121 | // TODO: should support it |
| 122 | if !used.insert(p.proto.get_number()) { |
| 123 | continue; |
| 124 | } |
| 125 | r.push(EnumValueGen::parse( |
| 126 | p.proto, |
| 127 | &self.type_name, |
| 128 | p.rust_name().get(), |
| 129 | )); |
| 130 | } |
| 131 | r |
| 132 | } |
| 133 | |
| 134 | // find enum value by name |
| 135 | pub fn value_by_name(&'a self, name: &str) -> EnumValueGen { |
| 136 | let v = self.enum_with_scope.value_by_name(name); |
| 137 | EnumValueGen::parse(v.proto, &self.type_name, v.rust_name().get()) |
| 138 | } |
| 139 | |
| 140 | pub fn write(&self, w: &mut CodeWriter) { |
| 141 | self.write_struct(w); |
| 142 | if self.allow_alias() { |
| 143 | w.write_line(""); |
| 144 | self.write_impl_eq(w); |
| 145 | w.write_line(""); |
| 146 | self.write_impl_hash(w); |
| 147 | } |
| 148 | w.write_line(""); |
| 149 | self.write_impl_enum(w); |
| 150 | w.write_line(""); |
| 151 | self.write_impl_copy(w); |
| 152 | w.write_line(""); |
| 153 | self.write_impl_default(w); |
| 154 | w.write_line(""); |
| 155 | self.write_impl_value(w); |
| 156 | } |
| 157 | |
| 158 | fn write_struct(&self, w: &mut CodeWriter) { |
| 159 | let mut derive = Vec::new(); |
| 160 | derive.push("Clone"); |
| 161 | if !self.allow_alias() { |
| 162 | derive.push("PartialEq"); |
| 163 | } |
| 164 | derive.push("Eq"); |
| 165 | derive.push("Debug"); |
| 166 | if !self.allow_alias() { |
| 167 | derive.push("Hash"); |
| 168 | } else { |
| 169 | w.comment("Note: you cannot use pattern matching for enums with allow_alias option"); |
| 170 | } |
| 171 | w.derive(&derive); |
| 172 | serde::write_serde_attr(w, &self.customize, "derive(Serialize, Deserialize)"); |
| 173 | let ref type_name = self.type_name; |
| 174 | w.expr_block(&format!("pub enum {}", type_name), |w| { |
| 175 | for value in self.values_all() { |
| 176 | if self.allow_alias() { |
| 177 | w.write_line(&format!( |
| 178 | "{}, // {}", |
| 179 | value.rust_name_inner(), |
| 180 | value.number() |
| 181 | )); |
| 182 | } else { |
| 183 | w.write_line(&format!( |
| 184 | "{} = {},", |
| 185 | value.rust_name_inner(), |
| 186 | value.number() |
| 187 | )); |
| 188 | } |
| 189 | } |
| 190 | }); |
| 191 | } |
| 192 | |
| 193 | fn write_fn_value(&self, w: &mut CodeWriter) { |
| 194 | w.def_fn("value(&self) -> i32", |w| { |
| 195 | if self.allow_alias() { |
| 196 | w.match_expr("*self", |w| { |
| 197 | for value in self.values_all() { |
| 198 | w.case_expr(value.rust_name_outer(), format!("{}", value.number())); |
| 199 | } |
| 200 | }); |
| 201 | } else { |
| 202 | w.write_line("*self as i32") |
| 203 | } |
| 204 | }); |
| 205 | } |
| 206 | |
| 207 | fn write_impl_enum(&self, w: &mut CodeWriter) { |
| 208 | let ref type_name = self.type_name; |
| 209 | w.impl_for_block("::protobuf::ProtobufEnum", &type_name, |w| { |
| 210 | self.write_fn_value(w); |
| 211 | |
| 212 | w.write_line(""); |
| 213 | let ref type_name = self.type_name; |
| 214 | w.def_fn( |
| 215 | &format!( |
| 216 | "from_i32(value: i32) -> ::std::option::Option<{}>", |
| 217 | type_name |
| 218 | ), |
| 219 | |w| { |
| 220 | w.match_expr("value", |w| { |
| 221 | let values = self.values_unique(); |
| 222 | for value in values { |
| 223 | w.write_line(&format!( |
| 224 | "{} => ::std::option::Option::Some({}),", |
| 225 | value.number(), |
| 226 | value.rust_name_outer() |
| 227 | )); |
| 228 | } |
| 229 | w.write_line(&format!("_ => ::std::option::Option::None")); |
| 230 | }); |
| 231 | }, |
| 232 | ); |
| 233 | |
| 234 | w.write_line(""); |
| 235 | w.def_fn(&format!("values() -> &'static [Self]"), |w| { |
| 236 | w.write_line(&format!("static values: &'static [{}] = &[", type_name)); |
| 237 | w.indented(|w| { |
| 238 | for value in self.values_all() { |
| 239 | w.write_line(&format!("{},", value.rust_name_outer())); |
| 240 | } |
| 241 | }); |
| 242 | w.write_line("];"); |
| 243 | w.write_line("values"); |
| 244 | }); |
| 245 | |
| 246 | if !self.lite_runtime { |
| 247 | w.write_line(""); |
| 248 | w.def_fn( |
| 249 | &format!( |
| 250 | "enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor" |
| 251 | ), |
| 252 | |w| { |
| 253 | w.lazy_static_decl_get( |
| 254 | "descriptor", |
| 255 | "::protobuf::reflect::EnumDescriptor", |
| 256 | |w| { |
| 257 | let ref type_name = self.type_name; |
| 258 | w.write_line(&format!( |
| 259 | "::protobuf::reflect::EnumDescriptor::new_pb_name::<{}>(\"{}\", {})", |
| 260 | type_name, |
| 261 | self.enum_with_scope.name_to_package(), |
| 262 | file_descriptor_proto_expr(&self.enum_with_scope.scope) |
| 263 | )); |
| 264 | }, |
| 265 | ); |
| 266 | }, |
| 267 | ); |
| 268 | } |
| 269 | }); |
| 270 | } |
| 271 | |
| 272 | fn write_impl_value(&self, w: &mut CodeWriter) { |
| 273 | w.impl_for_block("::protobuf::reflect::ProtobufValue", &self.type_name, |w| { |
| 274 | w.def_fn( |
| 275 | "as_ref(&self) -> ::protobuf::reflect::ReflectValueRef", |
Haibo Huang | 72fec01 | 2020-07-10 20:24:04 -0700 | [diff] [blame^] | 276 | |w| w.write_line("::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))"), |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 277 | ) |
| 278 | }) |
| 279 | } |
| 280 | |
| 281 | fn write_impl_copy(&self, w: &mut CodeWriter) { |
| 282 | w.impl_for_block("::std::marker::Copy", &self.type_name, |_w| {}); |
| 283 | } |
| 284 | |
| 285 | fn write_impl_eq(&self, w: &mut CodeWriter) { |
| 286 | assert!(self.allow_alias()); |
Haibo Huang | 72fec01 | 2020-07-10 20:24:04 -0700 | [diff] [blame^] | 287 | w.impl_for_block( |
| 288 | "::std::cmp::PartialEq", |
| 289 | &format!("{}", self.type_name), |
| 290 | |w| { |
| 291 | w.def_fn("eq(&self, other: &Self) -> bool", |w| { |
| 292 | w.write_line(&format!( |
| 293 | "{}::ProtobufEnum::value(self) == {}::ProtobufEnum::value(other)", |
| 294 | protobuf_crate_path(&self.customize), |
| 295 | protobuf_crate_path(&self.customize) |
| 296 | )); |
| 297 | }); |
| 298 | }, |
| 299 | ); |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 300 | } |
| 301 | |
| 302 | fn write_impl_hash(&self, w: &mut CodeWriter) { |
| 303 | assert!(self.allow_alias()); |
Haibo Huang | 72fec01 | 2020-07-10 20:24:04 -0700 | [diff] [blame^] | 304 | w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| { |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 305 | w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| { |
Haibo Huang | 72fec01 | 2020-07-10 20:24:04 -0700 | [diff] [blame^] | 306 | w.write_line(&format!( |
| 307 | "state.write_i32({}::ProtobufEnum::value(self))", |
| 308 | protobuf_crate_path(&self.customize) |
| 309 | )); |
Chih-Hung Hsieh | 92ff605 | 2020-06-10 20:18:39 -0700 | [diff] [blame] | 310 | }); |
| 311 | }); |
| 312 | } |
| 313 | |
| 314 | fn write_impl_default(&self, w: &mut CodeWriter) { |
| 315 | let first_value = &self.enum_with_scope.values()[0]; |
| 316 | if first_value.proto.get_number() != 0 { |
| 317 | // This warning is emitted only for proto2 |
| 318 | // (because in proto3 first enum variant number is always 0). |
| 319 | // `Default` implemented unconditionally to simplify certain |
| 320 | // generic operations, e. g. reading a map. |
| 321 | // Also, note that even in proto2 some operations fallback to |
| 322 | // first enum value, e. g. `get_xxx` for unset field, |
| 323 | // so this implementation is not completely unreasonable. |
| 324 | w.comment("Note, `Default` is implemented although default value is not 0"); |
| 325 | } |
| 326 | w.impl_for_block("::std::default::Default", &self.type_name, |w| { |
| 327 | w.def_fn("default() -> Self", |w| { |
| 328 | w.write_line(&format!( |
| 329 | "{}::{}", |
| 330 | &self.type_name, |
| 331 | &first_value.rust_name() |
| 332 | )) |
| 333 | }); |
| 334 | }); |
| 335 | } |
| 336 | } |