Implement `is_known` method on the `Enum` trait
This is equivalent to C++'s `_IsValid` functions. Since proto3 enums are open, `is_known` seems to be a better name than the misleading `is_valid`. C++-specific Protocol Buffers documentation already uses "known fields" and "unknown enum values" expressions. PiperOrigin-RevId: 630344180
This commit is contained in:
Родитель
d9ff109888
Коммит
733b9c54e9
|
@ -19,9 +19,16 @@ use std::{
|
|||
/// representation as erased enums in the runtime.
|
||||
/// - For C++, this is `proto2::RepeatedField<c_int>`
|
||||
/// - For UPB, this is an array compatible with `int32`
|
||||
pub unsafe trait Enum {
|
||||
pub unsafe trait Enum: TryFrom<i32> {
|
||||
/// The name of the enum.
|
||||
const NAME: &'static str;
|
||||
|
||||
/// Returns `true` if the given numeric value matches one of the `Self`'s
|
||||
/// defined values.
|
||||
///
|
||||
/// If `Self` is a closed enum, then `TryFrom<i32>` for `value` succeeds if
|
||||
/// and only if this function returns `true`.
|
||||
fn is_known(value: i32) -> bool;
|
||||
}
|
||||
|
||||
/// An integer value wasn't known for an enum while converting.
|
||||
|
|
|
@ -22,7 +22,7 @@ use std::fmt;
|
|||
/// These are the items protobuf users can access directly.
|
||||
#[doc(hidden)]
|
||||
pub mod __public {
|
||||
pub use crate::r#enum::UnknownEnumValue;
|
||||
pub use crate::r#enum::{Enum, UnknownEnumValue};
|
||||
pub use crate::map::{Map, MapIter, MapMut, MapView, ProxiedInMapValue};
|
||||
pub use crate::optional::Optional;
|
||||
pub use crate::proto;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
use enums_proto::*;
|
||||
use googletest::prelude::*;
|
||||
use protobuf::Enum;
|
||||
use unittest_proto::*;
|
||||
|
||||
#[test]
|
||||
|
@ -167,3 +168,24 @@ fn test_enum_conversion_failure_impls_std_error() {
|
|||
let err = TestSparseEnum::try_from(1).unwrap_err();
|
||||
let _test_compiles: &dyn std::error::Error = &err;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_known_for_closed_enum() {
|
||||
assert_that!(test_all_types::NestedEnum::is_known(-2), eq(false));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(-1), eq(true));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(0), eq(false));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(1), eq(true));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(2), eq(true));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(3), eq(true));
|
||||
assert_that!(test_all_types::NestedEnum::is_known(4), eq(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_known_for_open_enum() {
|
||||
assert_that!(TestEnumWithNumericNames::is_known(-1), eq(false));
|
||||
assert_that!(TestEnumWithNumericNames::is_known(0), eq(true));
|
||||
assert_that!(TestEnumWithNumericNames::is_known(1), eq(true));
|
||||
assert_that!(TestEnumWithNumericNames::is_known(2), eq(true));
|
||||
assert_that!(TestEnumWithNumericNames::is_known(3), eq(true));
|
||||
assert_that!(TestEnumWithNumericNames::is_known(4), eq(false));
|
||||
}
|
||||
|
|
|
@ -325,23 +325,21 @@ void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc) {
|
|||
// The default value of an enum is the first listed value.
|
||||
// The compiler checks that this is equal to 0 for open enums.
|
||||
{"default_int_value", absl::StrCat(desc.value(0)->number())},
|
||||
{"known_values_pattern",
|
||||
// TODO: Check validity in UPB/C++.
|
||||
absl::StrJoin(values, "|",
|
||||
[](std::string* o, const RustEnumValue& val) {
|
||||
absl::StrAppend(o, val.number);
|
||||
})},
|
||||
{"impl_from_i32",
|
||||
[&] {
|
||||
if (desc.is_closed()) {
|
||||
ctx.Emit({{"name", name},
|
||||
{"known_values_pattern",
|
||||
// TODO: Check validity in UPB/C++.
|
||||
absl::StrJoin(
|
||||
values, "|",
|
||||
[](std::string* o, const RustEnumValue& val) {
|
||||
absl::StrAppend(o, val.number);
|
||||
})}},
|
||||
R"rs(
|
||||
ctx.Emit(R"rs(
|
||||
impl $std$::convert::TryFrom<i32> for $name$ {
|
||||
type Error = $pb$::UnknownEnumValue<Self>;
|
||||
|
||||
fn try_from(val: i32) -> Result<$name$, Self::Error> {
|
||||
if matches!(val, $known_values_pattern$) {
|
||||
if <Self as $pbi$::Enum>::is_known(val) {
|
||||
Ok(Self(val))
|
||||
} else {
|
||||
Err($pb$::UnknownEnumValue::new($pbi$::Private, val))
|
||||
|
@ -350,7 +348,7 @@ void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc) {
|
|||
}
|
||||
)rs");
|
||||
} else {
|
||||
ctx.Emit({{"name", name}}, R"rs(
|
||||
ctx.Emit(R"rs(
|
||||
impl $std$::convert::From<i32> for $name$ {
|
||||
fn from(val: i32) -> $name$ {
|
||||
Self(val)
|
||||
|
@ -457,6 +455,10 @@ void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc) {
|
|||
// SAFETY: this is an enum type
|
||||
unsafe impl $pbi$::Enum for $name$ {
|
||||
const NAME: &'static str = "$name$";
|
||||
|
||||
fn is_known(value: i32) -> bool {
|
||||
matches!(value, $known_values_pattern$)
|
||||
}
|
||||
}
|
||||
|
||||
$impl_proxied_in_map$
|
||||
|
|
Загрузка…
Ссылка в новой задаче