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![pub](Span::call_site())),
+        };
+        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