Skip to content

Commit ca73cbe

Browse files
authored
feat: add TryFrom<i32> implementation to Enumeration (#853)
1 parent 9c877ce commit ca73cbe

5 files changed

Lines changed: 70 additions & 14 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,21 +163,22 @@ The `#[derive(::prost::Enumeration)]` annotation added to the generated
163163
```rust,ignore
164164
impl PhoneType {
165165
pub fn is_valid(value: i32) -> bool { ... }
166+
#[deprecated]
166167
pub fn from_i32(value: i32) -> Option<PhoneType> { ... }
167168
}
168169
```
169170

170-
so you can convert an `i32` to its corresponding `PhoneType` value by doing,
171+
It also adds an `impl TryFrom<i32> for PhoneType`, so you can convert an `i32` to its corresponding `PhoneType` value by doing,
171172
for example:
172173

173174
```rust,ignore
174175
let phone_type = 2i32;
175176
176-
match PhoneType::from_i32(phone_type) {
177-
Some(PhoneType::Mobile) => ...,
178-
Some(PhoneType::Home) => ...,
179-
Some(PhoneType::Work) => ...,
180-
None => ...,
177+
match PhoneType::try_from(phone_type) {
178+
Ok(PhoneType::Mobile) => ...,
179+
Ok(PhoneType::Home) => ...,
180+
Ok(PhoneType::Work) => ...,
181+
Err(_) => ...,
181182
}
182183
```
183184

prost-derive/src/field/map.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,17 @@ impl Field {
275275
Some(quote! {
276276
#[doc=#get_doc]
277277
pub fn #get(&self, key: #key_ref_ty) -> ::core::option::Option<#ty> {
278-
self.#ident.get(#take_ref key).cloned().and_then(#ty::from_i32)
278+
self.#ident.get(#take_ref key).cloned().and_then(|x| {
279+
let result: Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
280+
result.ok()
281+
})
279282
}
280283
#[doc=#insert_doc]
281284
pub fn #insert(&mut self, key: #key_ty, value: #ty) -> ::core::option::Option<#ty> {
282-
self.#ident.insert(key, value as i32).and_then(#ty::from_i32)
285+
self.#ident.insert(key, value as i32).and_then(|x| {
286+
let result: Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
287+
result.ok()
288+
})
283289
}
284290
})
285291
} else {

prost-derive/src/field/scalar.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,10 @@ impl Field {
219219
struct #wrap_name<'a>(&'a i32);
220220
impl<'a> ::core::fmt::Debug for #wrap_name<'a> {
221221
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
222-
match #ty::from_i32(*self.0) {
223-
None => ::core::fmt::Debug::fmt(&self.0, f),
224-
Some(en) => ::core::fmt::Debug::fmt(&en, f),
222+
let res: Result<#ty, _> = ::core::convert::TryFrom::try_from(*self.0);
223+
match res {
224+
Err(_) => ::core::fmt::Debug::fmt(&self.0, f),
225+
Ok(en) => ::core::fmt::Debug::fmt(&en, f),
225226
}
226227
}
227228
}
@@ -296,7 +297,7 @@ impl Field {
296297
quote! {
297298
#[doc=#get_doc]
298299
pub fn #get(&self) -> #ty {
299-
#ty::from_i32(self.#ident).unwrap_or(#default)
300+
::core::convert::TryFrom::try_from(self.#ident).unwrap_or(#default)
300301
}
301302

302303
#[doc=#set_doc]
@@ -314,7 +315,10 @@ impl Field {
314315
quote! {
315316
#[doc=#get_doc]
316317
pub fn #get(&self) -> #ty {
317-
self.#ident.and_then(#ty::from_i32).unwrap_or(#default)
318+
self.#ident.and_then(|x| {
319+
let result: Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
320+
result.ok()
321+
}).unwrap_or(#default)
318322
}
319323

320324
#[doc=#set_doc]
@@ -336,7 +340,10 @@ impl Field {
336340
::core::iter::Cloned<::core::slice::Iter<i32>>,
337341
fn(i32) -> ::core::option::Option<#ty>,
338342
> {
339-
self.#ident.iter().cloned().filter_map(#ty::from_i32)
343+
self.#ident.iter().cloned().filter_map(|x| {
344+
let result: Result<#ty, _> = ::core::convert::TryFrom::try_from(x);
345+
result.ok()
346+
})
340347
}
341348
#[doc=#push_doc]
342349
pub fn #push(&mut self, value: #ty) {

prost-derive/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ fn try_enumeration(input: TokenStream) -> Result<TokenStream, Error> {
291291
|&(ref variant, ref value)| quote!(#value => ::core::option::Option::Some(#ident::#variant)),
292292
);
293293

294+
let try_from = variants.iter().map(
295+
|&(ref variant, ref value)| quote!(#value => ::core::result::Result::Ok(#ident::#variant)),
296+
);
297+
294298
let is_valid_doc = format!("Returns `true` if `value` is a variant of `{}`.", ident);
295299
let from_i32_doc = format!(
296300
"Converts an `i32` to a `{}`, or `None` if `value` is not a valid variant.",
@@ -307,6 +311,7 @@ fn try_enumeration(input: TokenStream) -> Result<TokenStream, Error> {
307311
}
308312
}
309313

314+
#[deprecated = "Use the TryFrom<i32> implementation instead"]
310315
#[doc=#from_i32_doc]
311316
pub fn from_i32(value: i32) -> ::core::option::Option<#ident> {
312317
match value {
@@ -327,6 +332,17 @@ fn try_enumeration(input: TokenStream) -> Result<TokenStream, Error> {
327332
value as i32
328333
}
329334
}
335+
336+
impl #impl_generics ::core::convert::TryFrom::<i32> for #ident #ty_generics #where_clause {
337+
type Error = ::prost::DecodeError;
338+
339+
fn try_from(value: i32) -> ::core::result::Result<#ident, ::prost::DecodeError> {
340+
match value {
341+
#(#try_from,)*
342+
_ => ::core::result::Result::Err(::prost::DecodeError::new("invalid enumeration value")),
343+
}
344+
}
345+
}
330346
};
331347

332348
Ok(expanded.into())

tests/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,32 @@ mod tests {
578578
);
579579
}
580580

581+
#[test]
582+
fn test_enum_try_from_i32() {
583+
use core::convert::TryFrom;
584+
use default_enum_value::{ERemoteClientBroadcastMsg, PrivacyLevel};
585+
586+
assert_eq!(Ok(PrivacyLevel::One), PrivacyLevel::try_from(1));
587+
assert_eq!(Ok(PrivacyLevel::Two), PrivacyLevel::try_from(2));
588+
assert_eq!(
589+
Ok(PrivacyLevel::PrivacyLevelThree),
590+
PrivacyLevel::try_from(3)
591+
);
592+
assert_eq!(
593+
Ok(PrivacyLevel::PrivacyLevelprivacyLevelFour),
594+
PrivacyLevel::try_from(4)
595+
);
596+
assert_eq!(
597+
Err(prost::DecodeError::new("invalid enumeration value")),
598+
PrivacyLevel::try_from(5)
599+
);
600+
601+
assert_eq!(
602+
Ok(ERemoteClientBroadcastMsg::KERemoteClientBroadcastMsgDiscovery),
603+
ERemoteClientBroadcastMsg::try_from(0)
604+
);
605+
}
606+
581607
#[test]
582608
fn test_default_string_escape() {
583609
let msg = default_string_escape::Person::default();

0 commit comments

Comments
 (0)