Skip to content

Commit

Permalink
Implement v0.6 enum definitions
Browse files Browse the repository at this point in the history
This does not implement accessors, just the enum definitions themselves.

PiperOrigin-RevId: 591039910
  • Loading branch information
kupiakos authored and copybara-github committed Dec 19, 2023
1 parent f75fe9e commit f08a836
Show file tree
Hide file tree
Showing 16 changed files with 990 additions and 6 deletions.
1 change: 1 addition & 0 deletions rust/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ rust_library(
#
# shared.rs is the root of the crate and has public items re-exported in protobuf.rs for user use.
PROTOBUF_SHARED = [
"enum.rs",
"internal.rs",
"macros.rs",
"optional.rs",
Expand Down
33 changes: 31 additions & 2 deletions rust/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
// Rust Protobuf runtime using the C++ kernel.

use crate::ProtoStr;
use crate::__internal::{Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
use crate::{Mut, ProxiedInRepeated, Repeated, View};
use crate::__internal::{Enum, Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
use crate::{Mut, ProxiedInRepeated, Repeated, RepeatedMut, RepeatedView, View};
use core::fmt::Debug;
use paste::paste;
use std::alloc::Layout;
use std::cell::UnsafeCell;
use std::convert::identity;
use std::ffi::c_int;
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
Expand Down Expand Up @@ -287,6 +288,34 @@ macro_rules! impl_repeated_primitives {

impl_repeated_primitives!(i32, u32, i64, u64, f32, f64, bool);

/// Cast a `RepeatedView<SomeEnum>` to `RepeatedView<c_int>`.
pub fn cast_enum_repeated_view<E: Enum + ProxiedInRepeated>(
private: Private,
repeated: RepeatedView<E>,
) -> RepeatedView<c_int> {
// SAFETY: the implementer of `Enum` has promised that this
// raw repeated is a type-erased `proto2::RepeatedField<int>*`.
unsafe { RepeatedView::from_raw(private, repeated.as_raw(Private)) }
}

/// Cast a `RepeatedMut<SomeEnum>` to `RepeatedMut<c_int>`.
///
/// Writing an unknown value is sound because all enums
/// are representationally open.
pub fn cast_enum_repeated_mut<E: Enum + ProxiedInRepeated>(
private: Private,
mut repeated: RepeatedMut<E>,
) -> RepeatedMut<i32> {
// SAFETY: the implementer of `Enum` has promised that this
// raw repeated is a type-erased `proto2::RepeatedField<int>*`.
unsafe {
RepeatedMut::from_inner(
private,
InnerRepeatedMut { raw: repeated.as_raw(Private), _phantom: PhantomData },
)
}
}

#[derive(Debug)]
pub struct MapInner<'msg, K: ?Sized, V: ?Sized> {
pub raw: RawMap,
Expand Down
51 changes: 51 additions & 0 deletions rust/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

use crate::__internal::Private;
use std::{
error::Error,
fmt::{Debug, Display},
marker::PhantomData,
};

/// Implemented by all generated enum types.
///
/// # Safety
/// - A `RepeatedView<Self>` or `RepeatedMut<Self>` must have the same internal
/// 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 {
/// The name of the enum.
const NAME: &'static str;
}

/// An integer value wasn't known for an enum while converting.
pub struct UnknownEnumValue<T>(i32, PhantomData<T>);

impl<T> UnknownEnumValue<T> {
#[doc(hidden)]
pub fn new(_private: Private, unknown_value: i32) -> Self {
Self(unknown_value, PhantomData)
}
}

impl<T> Debug for UnknownEnumValue<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("UnknownEnumValue").field(&self.0).finish()
}
}

impl<T: Enum> Display for UnknownEnumValue<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let val = self.0;
let enum_name = T::NAME;
write!(f, "{val} is not a known value for {enum_name}")
}
}

impl<T: Enum> Error for UnknownEnumValue<T> {}
1 change: 1 addition & 0 deletions rust/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! exposed to through the `protobuf` path but must be public for use by
//! generated code.

pub use crate::r#enum::Enum;
pub use crate::vtable::{
new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, PrimitiveOptionalMutVTable,
PrimitiveVTable, PrimitiveWithRawVTable, RawVTableMutator, RawVTableOptionalMutatorData,
Expand Down
3 changes: 3 additions & 0 deletions rust/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +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::map::{Map, MapMut, MapView};
pub use crate::optional::{AbsentField, FieldEntry, Optional, PresentField};
pub use crate::primitive::PrimitiveMut;
Expand Down Expand Up @@ -49,6 +50,8 @@ pub mod __runtime;
#[path = "upb.rs"]
pub mod __runtime;

#[path = "enum.rs"]
mod r#enum;
mod macros;
mod map;
mod optional;
Expand Down
27 changes: 27 additions & 0 deletions rust/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,33 @@ rust_upb_proto_library(
deps = [":edition2023_proto"],
)

proto_library(
name = "enums_proto",
testonly = True,
srcs = ["enums.proto"],
deps = ["//devtools/staticanalysis/pipeline/analyzers/proto_best_practices/proto:optouts_proto"],
)

cc_proto_library(
name = "enums_cc_proto",
testonly = True,
deps = [":enums_proto"],
)

rust_cc_proto_library(
name = "enums_cc_rust_proto",
testonly = True,
visibility = ["//rust/test/shared:__subpackages__"],
deps = [":enums_cc_proto"],
)

rust_upb_proto_library(
name = "enums_upb_rust_proto",
testonly = True,
visibility = ["//rust/test/shared:__subpackages__"],
deps = [":enums_proto"],
)

proto_library(
name = "no_package_import_proto",
testonly = True,
Expand Down
61 changes: 61 additions & 0 deletions rust/test/enums.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// LINT: LEGACY_NAMES

// The names in this file are meant to test edge cases.
syntax = "proto3";

package enums;

import "devtools/staticanalysis/pipeline/analyzers/proto_best_practices/proto/optouts.proto";

option (proto_best_practices.file_optouts) = {
categories: ENUM_DEFAULT_VALUE_NAME_CONFLICT
categories: ENUM_VALUE_NAMES
};

// This should result in an enum with these accessible values:
// - Unknown = 0
// - Unrecognized = 0
// - Foo = 1
// - Bar = 2
// - DifferentNameAlias = 2
enum TestEnumWithDuplicateStrippedPrefixNames {
option allow_alias = true;

UNKNOWN = 0;
TestEnumWithDuplicateStrippedPrefixNamesUNRECOGNIZED = 0;

TestEnumWithDuplicateStrippedPrefixNames_FOO = 1;
TEST_ENUM_WITH_DUPLICATE_STRIPPED_PREFIX_NAMES_FOO = 1;
FOO = 1;

TestEnumWithDuplicateStrippedPrefixNamesBAR = 2;
TEST_ENUM_WITH_DUPLICATE_STRIPPED_PREFIX_NAMESBar = 2;
BAR = 2;
TEST_ENUM_WITH_DUPLICATE_STRIPPED_PREFIX_NAMES_DIFFERENT_NAME_ALIAS = 2;
}

// This should result in an enum with these accessible values:
// - Unknown = 0
// - _2020 = 1
// - _2021 = 2
// - _2022 = 3
enum TestEnumWithNumericNames {
TestEnumWithNumericNamesUNKNOWN = 0;
TestEnumWithNumericNames_2020 = 1;
TEST_ENUM_WITH_NUMERIC_NAMES_2021 = 2;
TEST_ENUM_WITH_NUMERIC_NAMES2022 = 3;
}

// This should result in an enum with these accessible values:
// - Unknown = 0
// - TestEnumValueNameSameAsEnum = 1
enum TestEnumValueNameSameAsEnum {
TEST_ENUM_VALUE_NAME_SAME_AS_ENUM_UNKNOWN = 0;
TEST_ENUM_VALUE_NAME_SAME_AS_ENUM = 1;
}
36 changes: 36 additions & 0 deletions rust/test/shared/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,42 @@ rust_test(
],
)

rust_test(
name = "enum_cpp_test",
srcs = ["enum_test.rs"],
aliases = {
"//rust:protobuf_cpp": "protobuf",
},
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
"not_build:arm",
],
deps = [
"@crate_index//:googletest",
"//rust:protobuf_cpp",
"//rust/test:enums_cc_rust_proto",
"//rust/test:unittest_cc_rust_proto",
],
)

rust_test(
name = "enum_upb_test",
srcs = ["enum_test.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
},
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
"not_build:arm",
],
deps = [
"@crate_index//:googletest",
"//rust:protobuf_upb",
"//rust/test:enums_upb_rust_proto",
"//rust/test:unittest_upb_rust_proto",
],
)

rust_test(
name = "package_cpp_test",
srcs = ["package_test.rs"],
Expand Down
Loading

0 comments on commit f08a836

Please sign in to comment.