Import 'open-enum' and 'open-enum-derive' crates
Request Document: go/android-rust-importing-crates
For CL Reviewers: go/android3p#cl-review
For Build Team: go/ab-third-party-imports
Bug: 382332368
Test: m libopen_enum
Change-Id: Ibbd4ede719b0af5b6f9a1789146f6a52cf16cbe5
diff --git a/crates/open-enum-derive/.android-checksum.json b/crates/open-enum-derive/.android-checksum.json
new file mode 100644
index 0000000..3a9ec46
--- /dev/null
+++ b/crates/open-enum-derive/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{"Cargo.toml":"386ebecc5d800cf1948e6bf153af7736c1cb76d74dc343e53b058879e8844956","METADATA":"b24505f301ecbe22487a7a5e7ccff1981ec940d1bc97f2508fb1308978cc893a","src/discriminant.rs":"20a13bd4e8055152b0fc95f2d7ba95227443984464f534540a05ad851f45ce10","src/lib.rs":"72f5c06aefbac100ace380e3944c8de7a11b7cef84202a8dcec2da24409ad843","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e","src/repr.rs":"cb8eabfdae5daf2b42979565af3179b85746b14594cea983968b20c7d75d0067","Android.bp":"f6c8e5f7ceafd37c2d181a57fb15100b3832d51dffd416f241a658dc9a8dc4e1","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","src/config.rs":"50cea8e39d5718ab0ffef5c748dd234d8062435e237d595e2e16a223fc4e6a48",".cargo-checksum.json":"9ae9d0a6e0fd598e4fe78d70fcad77efca60633c5ba1b7929d2ac69336801628"}}
\ No newline at end of file
diff --git a/crates/open-enum-derive/.cargo-checksum.json b/crates/open-enum-derive/.cargo-checksum.json
new file mode 100644
index 0000000..1120000
--- /dev/null
+++ b/crates/open-enum-derive/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"5a5836f65730437a51b36e53d35b1004d37a788d37704174423fdfb0c806caca","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","src/config.rs":"361e7d626cf48139e523d9d8adf8bed0125358f74b0d097ef60e437dee925c0d","src/discriminant.rs":"8a51087ab5e913b8a8158ec6db63a451b868b437fc51da5eeb8bc821cf566c0e","src/lib.rs":"a0748c299c765bb15da42a680c4ecca1fb4338d88f8d0738e55196ccb7df442f","src/repr.rs":"43ad9dc75bd05bfa916feca32f7ec07d29e926330b00b43a1eedc5fe1d849961"},"package":"8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"}
\ No newline at end of file
diff --git a/crates/open-enum-derive/Android.bp b/crates/open-enum-derive/Android.bp
new file mode 100644
index 0000000..0d22ad1
--- /dev/null
+++ b/crates/open-enum-derive/Android.bp
@@ -0,0 +1,30 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file because the changes will be overridden on upgrade.
+
+package {
+ default_applicable_licenses: ["external_rust_crates_open-enum-derive_license"],
+ default_team: "trendy_team_android_rust",
+}
+
+license {
+ name: "external_rust_crates_open-enum-derive_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ license_text: ["LICENSE"],
+}
+
+rust_proc_macro {
+ name: "libopen_enum_derive",
+ crate_name: "open_enum_derive",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.5.2",
+ crate_root: "src/lib.rs",
+ edition: "2021",
+ rustlibs: [
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+ product_available: true,
+ vendor_available: true,
+}
diff --git a/crates/open-enum-derive/Cargo.toml b/crates/open-enum-derive/Cargo.toml
new file mode 100644
index 0000000..908b66d
--- /dev/null
+++ b/crates/open-enum-derive/Cargo.toml
@@ -0,0 +1,50 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "open-enum-derive"
+version = "0.5.2"
+include = [
+ "/src",
+ "LICENSE",
+ "Cargo.toml",
+]
+description = "An attribute for generating \"open\" C-like enums, those that accept any integer value, by using a newtype struct and associated constants"
+readme = "README.md"
+keywords = [
+ "enum",
+ "open",
+ "integer",
+ "newtype",
+]
+categories = [
+ "no-std",
+ "rust-patterns",
+]
+license = "Apache-2.0"
+repository = "https://github.com/kupiakos/open-enum/tree/main/derive"
+
+[lib]
+proc-macro = true
+
+[dependencies.proc-macro2]
+version = "1.0.43"
+
+[dependencies.quote]
+version = "1"
+
+[dependencies.syn]
+version = "2"
+features = ["full"]
+
+[features]
+repr_c = []
diff --git a/crates/open-enum-derive/LICENSE b/crates/open-enum-derive/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/crates/open-enum-derive/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/crates/open-enum-derive/METADATA b/crates/open-enum-derive/METADATA
new file mode 100644
index 0000000..903e2d7
--- /dev/null
+++ b/crates/open-enum-derive/METADATA
@@ -0,0 +1,20 @@
+name: "open-enum-derive"
+description: "An attribute for generating \"open\" C-like enums, those that accept any integer value, by using a newtype struct and associated constants"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "open-enum-derive"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/open-enum-derive/open-enum-derive-0.5.2.crate"
+ primary_source: true
+ }
+ version: "0.5.2"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2024
+ month: 12
+ day: 13
+ }
+}
diff --git a/crates/open-enum-derive/MODULE_LICENSE_APACHE2 b/crates/open-enum-derive/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/crates/open-enum-derive/MODULE_LICENSE_APACHE2
diff --git a/crates/open-enum-derive/cargo_embargo.json b/crates/open-enum-derive/cargo_embargo.json
new file mode 100644
index 0000000..cb908d7
--- /dev/null
+++ b/crates/open-enum-derive/cargo_embargo.json
@@ -0,0 +1,3 @@
+{
+ "run_cargo": false
+}
diff --git a/crates/open-enum-derive/src/config.rs b/crates/open-enum-derive/src/config.rs
new file mode 100644
index 0000000..ebaec63
--- /dev/null
+++ b/crates/open-enum-derive/src/config.rs
@@ -0,0 +1,83 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::collections::HashSet;
+
+use proc_macro2::{Ident, Span};
+use quote::ToTokens;
+use syn::{parse::Parse, Error, Token, Visibility};
+
+pub struct Config {
+ pub allow_alias: bool,
+ pub repr_visibility: Visibility,
+}
+
+impl Parse for Config {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ let mut out = Self {
+ allow_alias: false,
+ repr_visibility: Visibility::Public(Token)),
+ };
+ let mut seen_names = HashSet::new();
+ while !input.is_empty() {
+ let name: Ident = input.parse()?;
+ let name_string = name.to_token_stream().to_string();
+ let has_value = input.peek(Token![=]);
+ if has_value {
+ let _eq_token: Token![=] = input.parse()?;
+ }
+ match name_string.as_str() {
+ "allow_alias" => {
+ if has_value {
+ let allow_alias: syn::LitBool = input.parse()?;
+ out.allow_alias = allow_alias.value;
+ } else {
+ out.allow_alias = true;
+ }
+ }
+ name_str @ "inner_vis" if !has_value => {
+ return Err(Error::new(
+ name.span(),
+ format!("Option `{name_str}` requires a value"),
+ ))
+ }
+ "inner_vis" => {
+ out.repr_visibility = input.parse()?;
+ if matches!(out.repr_visibility, syn::Visibility::Inherited) {
+ return Err(input.error("Expected visibility"));
+ }
+ }
+ unknown_name => {
+ return Err(Error::new(
+ name.span(),
+ format!("Unknown option `{unknown_name}`"),
+ ));
+ }
+ }
+ if !input.is_empty() {
+ let _comma: Token![,] = input.parse()?;
+ }
+ if !seen_names.insert(name_string) {
+ return Err(Error::new(
+ name.span(),
+ format!(
+ "Option `{name}` listed more than once",
+ name = name.to_token_stream()
+ ),
+ ));
+ }
+ }
+ Ok(out)
+ }
+}
diff --git a/crates/open-enum-derive/src/discriminant.rs b/crates/open-enum-derive/src/discriminant.rs
new file mode 100644
index 0000000..f4e31eb
--- /dev/null
+++ b/crates/open-enum-derive/src/discriminant.rs
@@ -0,0 +1,84 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use proc_macro2::{Literal, TokenStream};
+use quote::{quote, ToTokens};
+use syn::Expr;
+
+#[derive(Clone)]
+pub enum Discriminant {
+ Literal(i128),
+ Nonliteral { base: Box<Expr>, offset: u32 },
+}
+
+impl Discriminant {
+ pub fn new(discriminant_expr: Expr) -> syn::Result<Self> {
+ // Positive literal int
+ if let syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Int(lit),
+ ..
+ }) = &discriminant_expr
+ {
+ return Ok(Discriminant::Literal(lit.base10_parse()?));
+ }
+
+ // Negative literal int
+ if let syn::Expr::Unary(syn::ExprUnary {
+ op: syn::UnOp::Neg(_),
+ expr,
+ ..
+ }) = &discriminant_expr
+ {
+ if let syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Int(lit),
+ ..
+ }) = &**expr
+ {
+ return Ok(Discriminant::Literal(-lit.base10_parse()?));
+ }
+ }
+
+ // Nonliteral expression
+ Ok(Discriminant::Nonliteral {
+ base: Box::new(discriminant_expr),
+ offset: 0,
+ })
+ }
+
+ pub fn next_value(self) -> Option<Self> {
+ Some(match self {
+ Discriminant::Literal(val) => Discriminant::Literal(val.checked_add(1)?),
+ Discriminant::Nonliteral { base, offset } => Discriminant::Nonliteral {
+ base,
+ offset: offset.checked_add(1)?,
+ },
+ })
+ }
+}
+
+impl ToTokens for Discriminant {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.extend(match self {
+ Discriminant::Literal(value) => Literal::i128_unsuffixed(*value).into_token_stream(),
+ Discriminant::Nonliteral { base, offset } => {
+ if *offset == 0 {
+ base.into_token_stream()
+ } else {
+ let offset = Literal::u32_unsuffixed(*offset);
+ quote!(#base + #offset)
+ }
+ }
+ })
+ }
+}
diff --git a/crates/open-enum-derive/src/lib.rs b/crates/open-enum-derive/src/lib.rs
new file mode 100644
index 0000000..cfcabe1
--- /dev/null
+++ b/crates/open-enum-derive/src/lib.rs
@@ -0,0 +1,343 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+extern crate proc_macro;
+
+mod config;
+mod discriminant;
+mod repr;
+
+use config::Config;
+
+use discriminant::Discriminant;
+use proc_macro2::{Span, TokenStream};
+use quote::{format_ident, quote, ToTokens};
+use repr::Repr;
+use std::collections::HashSet;
+use syn::Attribute;
+use syn::{
+ parse_macro_input, punctuated::Punctuated, spanned::Spanned, Error, Ident, ItemEnum, Visibility,
+};
+
+/// Sets the span for every token tree in the token stream
+fn set_token_stream_span(tokens: TokenStream, span: Span) -> TokenStream {
+ tokens
+ .into_iter()
+ .map(|mut tt| {
+ tt.set_span(span);
+ tt
+ })
+ .collect()
+}
+
+/// Checks that there are no duplicate discriminant values. If all variants are literals, return an `Err` so we can have
+/// more clear error messages. Otherwise, emit a static check that ensures no duplicates.
+fn check_no_alias<'a>(
+ enum_: &ItemEnum,
+ variants: impl Iterator<Item = (&'a Ident, &'a Discriminant, Span)> + Clone,
+) -> syn::Result<TokenStream> {
+ // If they're all literals, we can give better error messages by checking at proc macro time.
+ let mut values: HashSet<i128> = HashSet::new();
+ for (_, variant, span) in variants {
+ if let &Discriminant::Literal(value) = variant {
+ if !values.insert(value) {
+ return Err(Error::new(
+ span,
+ format!("discriminant value `{value}` assigned more than once"),
+ ));
+ }
+ } else {
+ let mut checking_enum = syn::ItemEnum {
+ ident: format_ident!("_Check{}", enum_.ident),
+ vis: Visibility::Inherited,
+ ..enum_.clone()
+ };
+ checking_enum.attrs.retain(|attr| {
+ matches!(
+ attr.path().to_token_stream().to_string().as_str(),
+ "repr" | "allow" | "warn" | "deny" | "forbid"
+ )
+ });
+ return Ok(quote!(
+ #[allow(dead_code)]
+ #checking_enum
+ ));
+ }
+ }
+ Ok(TokenStream::default())
+}
+
+fn emit_debug_impl<'a>(
+ ident: &Ident,
+ variants: impl Iterator<Item = &'a Ident> + Clone,
+ attrs: impl Iterator<Item = &'a Vec<Attribute>> + Clone,
+) -> TokenStream {
+ let attrs = attrs.map(|attrs| {
+ // Only allow "#[cfg(...)]" attributes
+ let iter = attrs.iter().filter(|attr| attr.path().is_ident("cfg"));
+ quote!(#(#iter)*)
+ });
+ quote!(impl ::core::fmt::Debug for #ident {
+ fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ #![allow(unreachable_patterns)]
+ let s = match *self {
+ #( #attrs Self::#variants => stringify!(#variants), )*
+ _ => {
+ return fmt.debug_tuple(stringify!(#ident)).field(&self.0).finish();
+ }
+ };
+ fmt.pad(s)
+ }
+ })
+}
+
+fn path_matches_prelude_derive(
+ got_path: &syn::Path,
+ expected_path_after_std: &[&'static str],
+) -> bool {
+ let &[a, b] = expected_path_after_std else {
+ unimplemented!("checking against stdlib paths with != 2 parts");
+ };
+ let segments: Vec<&syn::PathSegment> = got_path.segments.iter().collect();
+ if segments
+ .iter()
+ .any(|segment| !matches!(segment.arguments, syn::PathArguments::None))
+ {
+ return false;
+ }
+ match &segments[..] {
+ // `core::fmt::Debug` or `some_crate::module::Name`
+ [maybe_core_or_std, maybe_a, maybe_b] => {
+ (maybe_core_or_std.ident == "core" || maybe_core_or_std.ident == "std")
+ && maybe_a.ident == a
+ && maybe_b.ident == b
+ }
+ // `fmt::Debug` or `module::Name`
+ [maybe_a, maybe_b] => {
+ maybe_a.ident == a && maybe_b.ident == b && got_path.leading_colon.is_none()
+ }
+ // `Debug` or `Name``
+ [maybe_b] => maybe_b.ident == b && got_path.leading_colon.is_none(),
+ _ => false,
+ }
+}
+
+fn open_enum_impl(
+ enum_: ItemEnum,
+ Config {
+ allow_alias,
+ repr_visibility,
+ }: Config,
+) -> Result<TokenStream, Error> {
+ // Does the enum define a `#[repr()]`?
+ let mut struct_attrs: Vec<TokenStream> = Vec::with_capacity(enum_.attrs.len() + 5);
+ struct_attrs.push(quote!(#[allow(clippy::exhaustive_structs)]));
+
+ if !enum_.generics.params.is_empty() {
+ return Err(Error::new(enum_.generics.span(), "enum cannot be generic"));
+ }
+ let mut variants = Vec::with_capacity(enum_.variants.len());
+ let mut last_field = Discriminant::Literal(-1);
+ for variant in &enum_.variants {
+ if !matches!(variant.fields, syn::Fields::Unit) {
+ return Err(Error::new(variant.span(), "enum cannot contain fields"));
+ }
+
+ let (value, value_span) = if let Some((_, discriminant)) = &variant.discriminant {
+ let span = discriminant.span();
+ (Discriminant::new(discriminant.clone())?, span)
+ } else {
+ last_field = last_field
+ .next_value()
+ .ok_or_else(|| Error::new(variant.span(), "enum discriminant overflowed"))?;
+ (last_field.clone(), variant.ident.span())
+ };
+ last_field = value.clone();
+ variants.push((&variant.ident, value, value_span, &variant.attrs))
+ }
+
+ let mut impl_attrs: Vec<TokenStream> = vec![quote!(#[allow(non_upper_case_globals)])];
+ let mut explicit_repr: Option<Repr> = None;
+
+ // To make `match` seamless, derive(PartialEq, Eq) if they aren't already.
+ let mut extra_derives = vec![quote!(::core::cmp::PartialEq), quote!(::core::cmp::Eq)];
+
+ let mut make_custom_debug_impl = false;
+ for attr in &enum_.attrs {
+ let mut include_in_struct = true;
+ // Turns out `is_ident` does a `to_string` every time
+ match attr.path().to_token_stream().to_string().as_str() {
+ "derive" => {
+ if let Ok(derive_paths) =
+ attr.parse_args_with(Punctuated::<syn::Path, syn::Token![,]>::parse_terminated)
+ {
+ for derive in &derive_paths {
+ // These derives are treated specially
+ const PARTIAL_EQ_PATH: &[&str] = &["cmp", "PartialEq"];
+ const EQ_PATH: &[&str] = &["cmp", "Eq"];
+ const DEBUG_PATH: &[&str] = &["fmt", "Debug"];
+
+ if path_matches_prelude_derive(derive, PARTIAL_EQ_PATH)
+ || path_matches_prelude_derive(derive, EQ_PATH)
+ {
+ // This derive is always included, exclude it.
+ continue;
+ }
+ if path_matches_prelude_derive(derive, DEBUG_PATH) && !allow_alias {
+ make_custom_debug_impl = true;
+ // Don't include this derive since we're generating a special one.
+ continue;
+ }
+ extra_derives.push(derive.to_token_stream());
+ }
+ include_in_struct = false;
+ }
+ }
+ // Copy linting attribute to the impl.
+ "allow" | "warn" | "deny" | "forbid" => impl_attrs.push(attr.to_token_stream()),
+ "repr" => {
+ assert!(explicit_repr.is_none(), "duplicate explicit repr");
+ explicit_repr = Some(attr.parse_args()?);
+ include_in_struct = false;
+ }
+ "non_exhaustive" => {
+ // technically it's exhaustive if the enum covers the full integer range
+ return Err(Error::new(attr.path().span(), "`non_exhaustive` cannot be applied to an open enum; it is already non-exhaustive"));
+ }
+ _ => {}
+ }
+ if include_in_struct {
+ struct_attrs.push(attr.to_token_stream());
+ }
+ }
+
+ // The proper repr to type-check against
+ let typecheck_repr: Repr = explicit_repr.unwrap_or(Repr::Isize);
+
+ // The actual representation of the value.
+ let inner_repr = match explicit_repr {
+ Some(explicit_repr) => {
+ // If there is an explicit repr, emit #[repr(transparent)].
+ struct_attrs.push(quote!(#[repr(transparent)]));
+ explicit_repr
+ }
+ None => {
+ // If there isn't an explicit repr, determine an appropriate sized integer that will fit.
+ // Interpret all discriminant expressions as isize.
+ repr::autodetect_inner_repr(variants.iter().map(|v| &v.1))
+ }
+ };
+
+ if !extra_derives.is_empty() {
+ struct_attrs.push(quote!(#[derive(#(#extra_derives),*)]));
+ }
+
+ let alias_check = if allow_alias {
+ TokenStream::default()
+ } else {
+ check_no_alias(&enum_, variants.iter().map(|(i, v, s, _)| (*i, v, *s)))?
+ };
+
+ let syn::ItemEnum { ident, vis, .. } = enum_;
+
+ let debug_impl = if make_custom_debug_impl {
+ emit_debug_impl(
+ &ident,
+ variants.iter().map(|(i, _, _, _)| *i),
+ variants.iter().map(|(_, _, _, a)| *a),
+ )
+ } else {
+ TokenStream::default()
+ };
+
+ let fields = variants
+ .into_iter()
+ .map(|(name, value, value_span, attrs)| {
+ let mut value = value.into_token_stream();
+ value = set_token_stream_span(value, value_span);
+ let inner = if typecheck_repr == inner_repr {
+ value
+ } else {
+ quote!(::core::convert::identity::<#typecheck_repr>(#value) as #inner_repr)
+ };
+ quote!(
+ #(#attrs)*
+ pub const #name: #ident = #ident(#inner);
+ )
+ });
+
+ Ok(quote! {
+ #(#struct_attrs)*
+ #vis struct #ident(#repr_visibility #inner_repr);
+
+ #(#impl_attrs)*
+ impl #ident {
+ #(
+ #fields
+ )*
+ }
+ #debug_impl
+ #alias_check
+ })
+}
+
+#[proc_macro_attribute]
+pub fn open_enum(
+ attrs: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let enum_ = parse_macro_input!(input as syn::ItemEnum);
+ let config = parse_macro_input!(attrs as Config);
+ open_enum_impl(enum_, config)
+ .unwrap_or_else(Error::into_compile_error)
+ .into()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_path_matches_stdlib_derive() {
+ const DEBUG_PATH: &[&str] = &["fmt", "Debug"];
+
+ for success_case in [
+ "::core::fmt::Debug",
+ "::std::fmt::Debug",
+ "core::fmt::Debug",
+ "std::fmt::Debug",
+ "fmt::Debug",
+ "Debug",
+ ] {
+ assert!(
+ path_matches_prelude_derive(&syn::parse_str(success_case).unwrap(), DEBUG_PATH),
+ "{success_case}"
+ );
+ }
+
+ for fail_case in [
+ "::fmt::Debug",
+ "::Debug",
+ "zerocopy::AsBytes",
+ "::zerocopy::AsBytes",
+ "PartialEq",
+ "core::cmp::Eq",
+ ] {
+ assert!(
+ !path_matches_prelude_derive(&syn::parse_str(fail_case).unwrap(), DEBUG_PATH),
+ "{fail_case}"
+ );
+ }
+ }
+}
diff --git a/crates/open-enum-derive/src/repr.rs b/crates/open-enum-derive/src/repr.rs
new file mode 100644
index 0000000..3ff8f58
--- /dev/null
+++ b/crates/open-enum-derive/src/repr.rs
@@ -0,0 +1,153 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::discriminant::Discriminant;
+use proc_macro2::{Ident, TokenStream};
+use quote::ToTokens;
+use std::ops::RangeInclusive;
+use syn::{parse::Parse, Error};
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum Repr {
+ I8,
+ U8,
+ U16,
+ I16,
+ U32,
+ I32,
+ U64,
+ I64,
+ Usize,
+ Isize,
+ #[cfg(feature = "repr_c")]
+ C,
+}
+
+fn range_contains(x: &RangeInclusive<i128>, y: &RangeInclusive<i128>) -> bool {
+ x.contains(y.start()) && x.contains(y.end())
+}
+
+impl Repr {
+ const REPR_RANGES: &'static [(Repr, RangeInclusive<i128>)] = &[
+ (Repr::I8, (i8::MIN as i128)..=(i8::MAX as i128)),
+ (Repr::U8, (u8::MIN as i128)..=(u8::MAX as i128)),
+ (Repr::I16, (i16::MIN as i128)..=(i16::MAX as i128)),
+ (Repr::U16, (u16::MIN as i128)..=(u16::MAX as i128)),
+ (Repr::I32, (i32::MIN as i128)..=(i32::MAX as i128)),
+ (Repr::U32, (u32::MIN as i128)..=(u32::MAX as i128)),
+ (Repr::I64, (i64::MIN as i128)..=(i64::MAX as i128)),
+ (Repr::U64, (u64::MIN as i128)..=(u64::MAX as i128)),
+ (Repr::Isize, (isize::MIN as i128)..=(isize::MAX as i128)),
+ (Repr::Usize, (usize::MIN as i128)..=(usize::MAX as i128)),
+ ];
+
+ /// Finds the smallest repr that can fit this range, if any.
+ fn smallest_fitting_repr(range: RangeInclusive<i128>) -> Option<Self> {
+ // TODO: perhaps check this logic matches current rustc behavior?
+ for (repr, repr_range) in Self::REPR_RANGES {
+ if range_contains(repr_range, &range) {
+ return Some(*repr);
+ }
+ }
+ None
+ }
+
+ fn name(self) -> &'static str {
+ match self {
+ Repr::I8 => "i8",
+ Repr::U8 => "u8",
+ Repr::U16 => "u16",
+ Repr::I16 => "i16",
+ Repr::U32 => "u32",
+ Repr::I32 => "i32",
+ Repr::U64 => "u64",
+ Repr::I64 => "i64",
+ Repr::Usize => "usize",
+ Repr::Isize => "isize",
+ #[cfg(feature = "repr_c")]
+ Repr::C => "C",
+ }
+ }
+}
+
+impl ToTokens for Repr {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.extend([match self {
+ // Technically speaking, #[repr(C)] on an enum isn't always `c_int`,
+ // but those who care can fix it if they need.
+ #[cfg(feature = "repr_c")]
+ Repr::C => quote::quote!(::open_enum::__private::c_int),
+ x => x.name().parse::<TokenStream>().unwrap(),
+ }])
+ }
+}
+
+impl Parse for Repr {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ let ident: Ident = input.parse()?;
+ Ok(match ident.to_string().as_str() {
+ "i8" => Repr::I8,
+ "u8" => Repr::U8,
+ "i16" => Repr::I16,
+ "u16" => Repr::U16,
+ "i32" => Repr::I32,
+ "u32" => Repr::U32,
+ "i64" => Repr::I64,
+ "u64" => Repr::U64,
+ "usize" => Repr::Usize,
+ "isize" => Repr::Isize,
+ #[cfg(feature = "repr_c")]
+ "C" => Repr::C,
+ #[cfg(not(feature = "repr_c"))]
+ "C" => {
+ return Err(Error::new(
+ ident.span(),
+ "#[repr(C)] requires either the `std` or `libc_` feature",
+ ))
+ }
+ _ => {
+ return Err(Error::new(
+ ident.span(),
+ format!("unsupported repr `{ident}`"),
+ ))
+ }
+ })
+ }
+}
+
+/// Figure out what the internal representation of the enum should be given its variants.
+///
+/// If we don't have sufficient info to auto-shrink the internal repr, fallback to isize.
+pub fn autodetect_inner_repr<'a>(variants: impl Iterator<Item = &'a Discriminant>) -> Repr {
+ let mut variants = variants.peekable();
+ if variants.peek().is_none() {
+ // TODO: maybe use the unit type for a fieldless open enum without a #[repr]?
+ return Repr::Isize;
+ }
+ let mut min = i128::MAX;
+ let mut max = i128::MIN;
+ for value in variants {
+ match value {
+ &Discriminant::Literal(value) => {
+ min = min.min(value);
+ max = max.max(value);
+ }
+ Discriminant::Nonliteral { .. } => {
+ // No way to do fancy sizing here, fall back to isize.
+ return Repr::Isize;
+ }
+ }
+ }
+ Repr::smallest_fitting_repr(min..=max).unwrap_or(Repr::Isize)
+}
diff --git a/crates/open-enum/.android-checksum.json b/crates/open-enum/.android-checksum.json
new file mode 100644
index 0000000..55d62ff
--- /dev/null
+++ b/crates/open-enum/.android-checksum.json
@@ -0,0 +1 @@
+{"package":null,"files":{"MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","METADATA":"0554b29b7e8cd7a73471a125b93f34504ffb933ecdd998ccc66834d527e14e01","cargo_embargo.json":"8079757b714383232a6eec24b702eac8a9171b7af9f02570e33c6e6b7ef9d522","src/test-lints.rs":"bd5f115c0d7905ddbec6b0da5518531147a132afab4c2d8ccb47f13beb63ce7e","Android.bp":"73a0c19b8a75f3b7a01540df4d15b29fb91379078dcd0cc85d9dd519622a7df6","src/lib.rs":"75c51f4ca2951e9b79eebc3f6cf99bbc2b3abe46eb7503dcd7e4489d85d29ab8",".cargo-checksum.json":"8f554bfc68b05f539a372f349253cc66e8e22f02281c30aa424bd999d6e682f8","Cargo.toml":"df6f3802a7932c51fbac457310ca2580cfb61b787ddbf4001f89c732be3a41cd","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","Cargo.lock":"a76eeea5ef15f4c1385a6bca36d9140757e37f4bf27a0d01cd4c883f8ff42ebc"}}
\ No newline at end of file
diff --git a/crates/open-enum/.cargo-checksum.json b/crates/open-enum/.cargo-checksum.json
new file mode 100644
index 0000000..ee24673
--- /dev/null
+++ b/crates/open-enum/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"88e3af82331f6df2a97cc01a93cca5bab947c19bc072962ff6293dec6f4554d0","Cargo.toml":"4c88c040264d7f1fc421c58bed413b55c69b839367026dea5df8ac99a664736c","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","src/lib.rs":"e7f798fb50172235b8c40ab986f23ae0ad2e80e1ef987b9d2ece3bdba2de2474","src/test-lints.rs":"7ed22e920df38b4447e8a16f01a27d1c4c6e80fc4346a45a276e78a5949a7c65"},"package":"2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"}
\ No newline at end of file
diff --git a/crates/open-enum/Android.bp b/crates/open-enum/Android.bp
new file mode 100644
index 0000000..80a49b8
--- /dev/null
+++ b/crates/open-enum/Android.bp
@@ -0,0 +1,32 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file because the changes will be overridden on upgrade.
+
+package {
+ default_applicable_licenses: ["external_rust_crates_open-enum_license"],
+ default_team: "trendy_team_android_rust",
+}
+
+license {
+ name: "external_rust_crates_open-enum_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ license_text: ["LICENSE"],
+}
+
+rust_library {
+ name: "libopen_enum",
+ host_supported: true,
+ crate_name: "open_enum",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.5.2",
+ crate_root: "src/lib.rs",
+ edition: "2021",
+ features: ["std"],
+ proc_macros: ["libopen_enum_derive"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ product_available: true,
+ vendor_available: true,
+}
diff --git a/crates/open-enum/Cargo.lock b/crates/open-enum/Cargo.lock
new file mode 100644
index 0000000..80d6df0
--- /dev/null
+++ b/crates/open-enum/Cargo.lock
@@ -0,0 +1,219 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
+[[package]]
+name = "itoa"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+
+[[package]]
+name = "libc"
+version = "0.2.132"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
+
+[[package]]
+name = "once_cell"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
+
+[[package]]
+name = "open-enum"
+version = "0.5.2"
+dependencies = [
+ "libc",
+ "open-enum-derive",
+ "trybuild",
+ "zerocopy",
+]
+
+[[package]]
+name = "open-enum-derive"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "serde"
+version = "1.0.143"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.143"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.99",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "trybuild"
+version = "1.0.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7f408301c7480f9e6294eb779cfc907f54bd901a9660ef24d7f233ed5376485"
+dependencies = [
+ "glob",
+ "once_cell",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "termcolor",
+ "toml",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
diff --git a/crates/open-enum/Cargo.toml b/crates/open-enum/Cargo.toml
new file mode 100644
index 0000000..6927d9f
--- /dev/null
+++ b/crates/open-enum/Cargo.toml
@@ -0,0 +1,60 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "open-enum"
+version = "0.5.2"
+authors = ["Alyssa Haroldsen <kupiakos@google.com>"]
+include = [
+ "/src",
+ "/LICENSE",
+ "Cargo.toml",
+]
+description = "An attribute for generating \"open\" fieldless enums, those that accept any integer value, by using a newtype struct and associated constants"
+readme = "README.md"
+keywords = [
+ "enum",
+ "open",
+ "integer",
+ "newtype",
+]
+categories = [
+ "no-std",
+ "rust-patterns",
+]
+license = "Apache-2.0"
+repository = "https://github.com/kupiakos/open-enum"
+
+[[bin]]
+name = "test_lints"
+path = "src/test-lints.rs"
+
+[dependencies.libc]
+version = "0.2"
+optional = true
+
+[dependencies.open-enum-derive]
+version = "=0.5.2"
+
+[dev-dependencies.trybuild]
+version = "1"
+
+[dev-dependencies.zerocopy]
+version = "0.7.11"
+features = ["derive"]
+
+[features]
+libc_ = [
+ "libc",
+ "open-enum-derive/repr_c",
+]
+std = ["open-enum-derive/repr_c"]
diff --git a/crates/open-enum/LICENSE b/crates/open-enum/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/crates/open-enum/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/crates/open-enum/METADATA b/crates/open-enum/METADATA
new file mode 100644
index 0000000..5e20ae5
--- /dev/null
+++ b/crates/open-enum/METADATA
@@ -0,0 +1,20 @@
+name: "open-enum"
+description: "An attribute for generating \"open\" fieldless enums, those that accept any integer value, by using a newtype struct and associated constants"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "open-enum"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/open-enum/open-enum-0.5.2.crate"
+ primary_source: true
+ }
+ version: "0.5.2"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2024
+ month: 12
+ day: 13
+ }
+}
diff --git a/crates/open-enum/MODULE_LICENSE_APACHE2 b/crates/open-enum/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/crates/open-enum/MODULE_LICENSE_APACHE2
diff --git a/crates/open-enum/cargo_embargo.json b/crates/open-enum/cargo_embargo.json
new file mode 100644
index 0000000..0b9badc
--- /dev/null
+++ b/crates/open-enum/cargo_embargo.json
@@ -0,0 +1,9 @@
+{
+ "run_cargo": false,
+ "module_blocklist" : [
+ "test_lints"
+ ],
+ "features": [
+ "std"
+ ]
+}
diff --git a/crates/open-enum/src/lib.rs b/crates/open-enum/src/lib.rs
new file mode 100644
index 0000000..9c625f3
--- /dev/null
+++ b/crates/open-enum/src/lib.rs
@@ -0,0 +1,309 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Rust enums are _closed_, meaning that the integer value distinguishing an enum, its _discriminant_,
+//! must be one of the variants listed. If the integer value isn't one of those discriminants, it
+//! is considered immediate [undefined behavior][ub]. This is true for enums with and without fields.
+//!
+//! This has some disadvantages:
+//! - in constrained environments, closed enums can require premature runtime checks when using
+//! `TryFrom` to convert from an integer. This is doubly true if the value will be checked again
+//! at a later point, such as with a C library.
+//! - an outdated binary using an enum won't preserve the value of an unknown field when reserializing
+//! data without an extra `Unrecognized` value making the type more expensive than an integer.
+//! - it can introduce Undefined Behavior at unexpected times if the author is unfamiliar with
+//! the [rules of writing `unsafe` Rust][nomicon].
+//!
+//! In constrast, C++ [scoped enumerations][cpp-scoped-enum] are _open_, meaning that the enum is a
+//! strongly-typed integer that could hold any value, though with a scoped set of well-known values.
+//!
+//! The _open enum_ pattern lets you have this in Rust. With a [newtype][newtype] and associated constants,
+//! the [open_enum][open_enum] macro turns this enum declaration:
+//!
+//! ```
+//! # use open_enum::open_enum;
+//! #[open_enum]
+//! enum Color {
+//! Red,
+//! Green,
+//! Blue,
+//! Orange,
+//! Black,
+//! }
+//! ```
+//!
+//! into a tuple struct with associated constants:
+//!
+//! ```
+//! #[derive(PartialEq, Eq)] // In order to work in `match`.
+//! struct Color(pub i8); // Automatic integer type, can be specified with #[repr]
+//!
+//! impl Color {
+//! pub const Red: Self = Color(0);
+//! pub const Green: Self = Color(1);
+//! pub const Blue: Self = Color(2);
+//! pub const Orange: Self = Color(3);
+//! pub const Black: Self = Color(4);
+//! }
+//! ```
+//!
+//! There are clear readability benefits to using field-less `enum`s to represent enumerated integer data.
+//! It provides more type safety than a raw integer, the `enum` syntax is consise, and it provides a
+//! set of constants grouped under a type that can have methods.
+//!
+//! # Usage
+//! Usage is similar to regular `enum`s, but with some key differences.
+//!
+//! ```
+//! # use open_enum::open_enum;
+//! # #[open_enum]
+//! # #[derive(Debug)]
+//! # enum Color {
+//! # Red,
+//! # Green,
+//! # Blue,
+//! # Orange,
+//! # Black,
+//! # }
+//! // Construct an open enum with the same `EnumName::VariantName` syntax.
+//! let mut blood_of_angry_men = Color::Red;
+//!
+//! // Access the integer value with `.0`.
+//! // This does not work: `Color::Red as u8`.
+//! assert_eq!(blood_of_angry_men.0, 0);
+//!
+//! // Construct an open enum with an arbitrary integer value like any tuple struct.
+//! let dark_of_ages_past = Color(4);
+//!
+//! // open enums always implement `PartialEq` and `Eq`, unlike regular enums.
+//! assert_eq!(dark_of_ages_past, Color::Black);
+//!
+//! // This is outside of the known colors - but that's OK!
+//! let this_is_fine = Color(10);
+//!
+//! // A match is always non-exhaustive - requiring a wildcard branch.
+//! match this_is_fine {
+//! Color::Red => panic!("a world about to dawn"),
+//! Color::Green => panic!("grass"),
+//! Color::Blue => panic!("蒼: not to be confused with 緑"),
+//! Color::Orange => panic!("fun fact: the fruit name came first"),
+//! Color::Black => panic!("the night that ends at last"),
+//! // Wildcard branch, if we don't recognize the value. `x =>` also works.
+//! Color(value) => assert_eq!(value, 10),
+//! }
+//!
+//! // Unlike a regular enum, you can pass the discriminant as a reference.
+//! fn increment(x: &mut i8) {
+//! *x += 1;
+//! }
+//!
+//! increment(&mut blood_of_angry_men.0);
+//! // These aren't men, they're skinks!
+//! assert_eq!(blood_of_angry_men, Color::Green);
+//!
+//! ```
+//!
+//! ## Integer type
+//! `open_enum` will automatically determine an appropriately sized integer[^its-all-isize] to
+//! represent the enum, if possible[^nonliterals-are-hard]. To choose a specific representation, it's the same
+//! as a regular `enum`: add `#[repr(type)]`.
+//! You can also specify `#[repr(C)]` to choose a C `int`.[^repr-c-feature][^repr-c-weird]
+//!
+//! If you specify an explicit `repr`, the output struct will be `#[repr(transparent)]`.
+//!
+//! ```
+//! # use open_enum::open_enum;
+//! #[open_enum]
+//! #[repr(i16)]
+//! #[derive(Debug)]
+//! enum Fruit {
+//! Apple,
+//! Banana,
+//! Kumquat,
+//! Orange,
+//! }
+//!
+//! assert_eq!(Fruit::Banana.0, 1i16);
+//! assert_eq!(Fruit::Kumquat, Fruit(2));
+//!
+//! ```
+//! <div class="example-wrap" style="display:inline-block"><pre class="compile_fail" style="white-space:normal;font:inherit;">
+//!
+//! **Warning**: `open_enum` may change the automatic integer representation for a given enum
+//! in a future version with a minor version bump - it is not considered a breaking change.
+//! Do not depend on this type remaining stable - use an explicit `#[repr]` for stability.
+//!
+//! </pre></div>
+//!
+//! [^its-all-isize]: Like regular `enum`s, the declared discriminant for enums without an explicit `repr`
+//! is interpreted as an `isize` regardless of the automatic storage type chosen.
+//!
+//! [^nonliterals-are-hard]: This optimization fails if the `enum` declares a non-literal constant expression
+//! as one of its discriminant values, and falls back to `isize`. To avoid this, specify an explicit `repr`.
+//!
+//! [^repr-c-weird]: Note that this might not actually be the correct default `enum` size for C on all platforms,
+//! since the [compiler could choose something smaller than `int`](https://stackoverflow.com/a/366026).
+//!
+//! [^repr-c-feature]: This requires either the `std` or `libc_` feature (note the underscore)
+//!
+//! ## Aliasing variants
+//! Regular `enum`s cannot have multiple variants with the same discriminant.
+//! However, since `open_enum` produces associated constants, multiple
+//! names can represent the same integer value. By default, `open_enum`
+//! rejects aliasing variants, but it can be allowed with the `allow_alias` option:
+//!
+//! ```
+//! # use open_enum::open_enum;
+//! #[open_enum(allow_alias)]
+//! #[derive(Debug)]
+//! enum Character {
+//! Viola = 0,
+//! Cesario = 0,
+//! Sebastian,
+//! Orsino,
+//! Olivia,
+//! Malvolio,
+//! }
+//!
+//! assert_eq!(Character::Viola, Character::Cesario);
+//!
+//! ```
+//!
+//!
+//!
+//! # Custom debug implementation
+//! `open_enum` will generate a debug implementation that mirrors the standard `#[derive(Debug)]` for normal Rust enums
+//! by printing the name of the variant rather than the value contained, if the value is a named variant.
+//!
+//! However, if an enum has `#[open_enum(allow_alias)]` specified, the debug representation will be the numeric value only.
+//!
+//! For example, this given enum,
+//! ```
+//! # use open_enum::open_enum;
+//! #[open_enum]
+//! #[derive(Debug)]
+//! enum Fruit {
+//! Apple,
+//! Pear,
+//! Banana,
+//! Blueberry = 5,
+//! Raspberry,
+//! }
+//! ```
+//!
+//! will have the following debug implementation emitted:
+//! ```
+//! # use open_enum::open_enum;
+//! # #[open_enum]
+//! # enum Fruit {
+//! # Apple,
+//! # Pear,
+//! # Banana,
+//! # Blueberry = 5,
+//! # Raspberry,
+//! # }
+//! # impl ::core::fmt::Debug for Fruit {
+//! fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+//! #![allow(unreachable_patterns)]
+//! let s = match *self {
+//! Self::Apple => stringify!(Apple),
+//! Self::Pear => stringify!(Pear),
+//! Self::Banana => stringify!(Banana),
+//! Self::Blueberry => stringify!(Blueberry),
+//! Self::Raspberry => stringify!(Raspberry),
+//! _ => {
+//! return fmt.debug_tuple(stringify!(Fruit)).field(&self.0).finish();
+//! }
+//! };
+//! fmt.pad(s)
+//! }
+//! # }
+//! ```
+//!
+//! # Compared with `#[non_exhuastive]`
+//! The [`non_exhaustive`][non-exhaustive] attribute indicates that a type or variant
+//! may have more fields or variants added in the future. When applied to an `enum` (not its variants),
+//! it requires that foreign crates provide a wildcard arm when `match`ing.
+//! Since open enums are inherently non-exhaustive[^mostly-non-exhaustive], this attribute is incompatible
+//! with `open_enum`. Unlike `non_exhaustive`, open enums also require a wildcard branch on `match`es in
+//! the defining crate.
+//!
+//! [^mostly-non-exhaustive]: Unless the enum defines a variant for every value of its underlying integer.
+//!
+//! # Disadvantages of open enums
+//! - The kind listed in the source code, an `enum`, is not the same as the actual output, a `struct`,
+//! which could be confusing or hard to debug, since its usage is similar, but not exactly the same.
+//! - No niche optimization: `Option<Color>` is 1 byte as a regular enum,
+//! but 2 bytes as an open enum.
+//! - No pattern-matching assistance in rust-analyzer.
+//! - You must have a wildcard case when pattern matching.
+//! - `match`es that exist elsewhere won't break when you add a new variant,
+//! similar to `#[non_exhaustive]`. However, it also means you may accidentally
+//! forget to fill out a branch arm.
+//!
+//!
+//! [cpp-scoped-enum]: https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations
+//! [nomicon]: https://doc.rust-lang.org/nomicon/
+//! [non-exhaustive]: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
+//! [ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+
+#![no_std]
+
+/// Constructs an *open* enum from a Rust enum definition,
+/// allowing it to represent more than just its listed variants.
+///
+/// See the [crate documentation](crate) for more details.
+///
+/// # Example
+/// ```
+/// # use open_enum::open_enum;
+/// #[open_enum]
+/// #[derive(Debug)]
+/// enum Color {
+/// Red,
+/// Green,
+/// Blue,
+/// Orange,
+/// Black,
+/// }
+///
+/// assert_eq!(Color::Red, Color(0));
+/// assert_eq!(Color(10).0, 10);
+/// ```
+///
+/// # Options
+/// - `allow_alias[ = $bool]`: default `false`. Allows duplicate discriminant values for variants.
+/// - `inner_vis = $vis`: default `pub`. Specifies the visibility of the inner integer.
+///
+/// # Integer type
+/// `open_enum` configures the discriminant type by intercepting a `repr` attribute on the enum.
+/// If done, the open enum is `#[repr(transparent)]` over the provided integer type.
+/// Otherwise, variant discriminants are interpreted as `isize` and an automatic integer type chosen.
+///
+/// # `PartialEq`/`Eq`
+/// Open enums implement `PartialEq` and `Eq` in order to work in a `match` statement.
+pub use open_enum_derive::open_enum;
+
+/// Utility items only to be used by macros. Do not expect API stability.
+#[doc(hidden)]
+pub mod __private {
+ #[cfg(all(feature = "libc", not(feature = "std")))]
+ pub use libc::c_int;
+
+ #[cfg(feature = "std")]
+ extern crate std;
+
+ #[cfg(feature = "std")]
+ pub use std::os::raw::c_int;
+}
diff --git a/crates/open-enum/src/test-lints.rs b/crates/open-enum/src/test-lints.rs
new file mode 100644
index 0000000..ca6b45f
--- /dev/null
+++ b/crates/open-enum/src/test-lints.rs
@@ -0,0 +1,79 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Tests lints that should compile.
+//!
+//! Tests (unit or integration) don't trigger missing_docs, so this must be a binary or library.
+//! Binary is simpler. Alternatively, this could be part of the ui/compile-fail test fixture.
+
+/// Tests that basic attributes propagate, like documentation.
+pub mod docs {
+ #![deny(missing_docs)]
+ use open_enum::open_enum;
+
+ #[open_enum]
+ /// This struct has documentation.
+ pub enum ImportantLetters {
+ /// A is the first letter of the English alphabet.
+ A,
+
+ /// B is for Bananaphone.
+ B,
+ }
+}
+
+/// Tests that allow lints propagate through an open enum definition correctly.
+pub mod allow_lint_propagates {
+ #![deny(missing_docs)]
+ use open_enum::open_enum;
+
+ // Checks that local lints propagate correctly.
+ #[open_enum]
+ #[allow(missing_docs)]
+ pub enum HasLintTop {
+ A,
+ B,
+ }
+
+ #[allow(missing_docs)]
+ #[open_enum]
+ pub enum HasLintBottom {
+ A,
+ B,
+ }
+}
+
+pub mod clippy {
+ // We should pass this, as this is a newtype.
+ #![deny(clippy::exhaustive_structs)]
+
+ #[open_enum::open_enum]
+ pub enum Foo {
+ Bar,
+ Baz,
+ }
+}
+
+pub mod nonliteral {
+ #![deny(dead_code)]
+
+ #[open_enum::open_enum]
+ #[derive(PartialEq, Eq)] // for some reason this has to be here to get a dead_code lint to trigger
+ #[repr(u32)]
+ pub enum Fuzz {
+ Balls = 1 << 1,
+ }
+}
+
+fn main() {}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index e24fed8..3d8a375 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -304,6 +304,8 @@
"once_cell",
"oneshot-uniffi",
"oorandom",
+ "open-enum",
+ "open-enum-derive",
"openssl-macros",
"os_str_bytes",
"p9",
@@ -3582,6 +3584,26 @@
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
+name = "open-enum"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eb2508143a400b3361812094d987dd5adc81f0f5294a46491be648d6c94cab5"
+dependencies = [
+ "open-enum-derive",
+]
+
+[[package]]
+name = "open-enum-derive"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d1296fab5231654a5aec8bf9e87ba4e3938c502fc4c3c0425a00084c78944be"
+dependencies = [
+ "proc-macro2 1.0.92",
+ "quote 1.0.36",
+ "syn 2.0.90",
+]
+
+[[package]]
name = "openssl"
version = "0.10.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index beca25d..08daeff 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -220,6 +220,8 @@
once_cell = "=1.19.0"
oneshot-uniffi = "=0.1.6"
oorandom = "=11.1.3"
+open-enum = "=0.5.2"
+open-enum-derive = "=0.5.2"
openssl-macros = "=0.1.0"
os_str_bytes = "=6.4.1"
p9 = "=0.2.3"
diff --git a/pseudo_crate/crate-list.txt b/pseudo_crate/crate-list.txt
index 1504e67..4fe0ee6 100644
--- a/pseudo_crate/crate-list.txt
+++ b/pseudo_crate/crate-list.txt
@@ -212,6 +212,8 @@
once_cell
oneshot-uniffi
oorandom
+open-enum
+open-enum-derive
openssl-macros
os_str_bytes
p9