blob: 47ac49a459ac160973e70ebc8a0161a7320d4684 [file] [log] [blame]
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07001use std::collections::HashSet;
2
3use protobuf::descriptor::*;
4
5use super::code_writer::*;
6use super::customize::Customize;
7use file_descriptor::file_descriptor_proto_expr;
Haibo Huang72fec012020-07-10 20:24:04 -07008use inside::protobuf_crate_path;
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -07009use protobuf_name::ProtobufAbsolutePath;
10use rust_types_values::type_name_to_rust_relative;
11use scope::EnumWithScope;
12use scope::RootScope;
13use scope::WithScope;
14use serde;
15
16#[derive(Clone)]
17pub struct EnumValueGen {
18 proto: EnumValueDescriptorProto,
19 enum_rust_name: String,
20 variant_rust_name: String,
21}
22
23impl 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
55pub(crate) struct EnumGen<'a> {
56 enum_with_scope: &'a EnumWithScope<'a>,
57 type_name: String,
58 lite_runtime: bool,
59 customize: Customize,
60}
61
62impl<'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 Huang72fec012020-07-10 20:24:04 -0700276 |w| w.write_line("::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))"),
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700277 )
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 Huang72fec012020-07-10 20:24:04 -0700287 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 Hsieh92ff6052020-06-10 20:18:39 -0700300 }
301
302 fn write_impl_hash(&self, w: &mut CodeWriter) {
303 assert!(self.allow_alias());
Haibo Huang72fec012020-07-10 20:24:04 -0700304 w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700305 w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
Haibo Huang72fec012020-07-10 20:24:04 -0700306 w.write_line(&format!(
307 "state.write_i32({}::ProtobufEnum::value(self))",
308 protobuf_crate_path(&self.customize)
309 ));
Chih-Hung Hsieh92ff6052020-06-10 20:18:39 -0700310 });
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}