Merge remote-tracking branch 'origin/upstream' am: 74f70830fe

Original change: undetermined

Change-Id: I67d3b244f0c78396a092e10e1dfa6c82c0a15cf5
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..b708b8e
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "86d359f430ecd12be4d544f55bc68c27a3cda82f"
+  },
+  "path_in_vcs": "num_enum_derive"
+}
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..6d12ae8
--- /dev/null
+++ b/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"
+rust-version = "1.70.0"
+name = "num_enum_derive"
+version = "0.7.3"
+authors = [
+    "Daniel Wagner-Hall <[email protected]>",
+    "Daniel Henry-Mantilla <[email protected]>",
+    "Vincent Esche <[email protected]>",
+]
+description = "Internal implementation details for ::num_enum (Procedural macros to make inter-operation between primitives and enums easier)"
+readme = "README.md"
+keywords = []
+categories = []
+license = "BSD-3-Clause OR MIT OR Apache-2.0"
+repository = "https://github.com/illicitonion/num_enum"
+
+[package.metadata.docs.rs]
+features = ["external_doc"]
+
+[lib]
+proc-macro = true
+
+[dependencies.proc-macro-crate]
+version = ">= 1, <= 3"
+optional = true
+
+[dependencies.proc-macro2]
+version = "1.0.60"
+
+[dependencies.quote]
+version = "1"
+
+[dependencies.syn]
+version = "2"
+features = ["parsing"]
+
+[dev-dependencies.syn]
+version = "2"
+features = [
+    "extra-traits",
+    "parsing",
+]
+
+[features]
+complex-expressions = ["syn/full"]
+default = ["std"]
+external_doc = []
+std = ["proc-macro-crate"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..67e2770
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,41 @@
+[package]
+name = "num_enum_derive"
+version = "0.7.3"  # Keep in sync with num_enum.
+rust-version = "1.70.0"
+authors = [
+  "Daniel Wagner-Hall <[email protected]>",
+  "Daniel Henry-Mantilla <[email protected]>",
+  "Vincent Esche <[email protected]>",
+]
+description = "Internal implementation details for ::num_enum (Procedural macros to make inter-operation between primitives and enums easier)"
+edition = "2021"
+repository = "https://github.com/illicitonion/num_enum"
+keywords = []
+categories = []
+license = "BSD-3-Clause OR MIT OR Apache-2.0"
+
+[lib]
+proc-macro = true
+
+[features]
+# Don't depend on proc-macro-crate in no_std environments because it causes an awkward depndency
+# on serde with std.
+#
+# See https://github.com/illicitonion/num_enum/issues/18
+std = ["proc-macro-crate"]
+complex-expressions = ["syn/full"]
+external_doc = []
+
+default = ["std"]  # disable to use in a `no_std` environment
+
+[package.metadata.docs.rs]
+features = ["external_doc"]
+
+[dependencies]
+proc-macro2 = "1.0.60"
+proc-macro-crate = { version = ">= 1, <= 3", optional = true }
+quote = "1"
+syn = { version = "2", features = ["parsing"] }
+
+[dev-dependencies]
+syn = { version = "2", features = ["extra-traits", "parsing"] }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..782c8c2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,232 @@
+                              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
+
+-----
+
+Copyright (c) 2018, Daniel Wagner-Hall
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of num_enum nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..1b5ec8b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,176 @@
+                              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
diff --git a/LICENSE-BSD b/LICENSE-BSD
new file mode 100644
index 0000000..b742e29
--- /dev/null
+++ b/LICENSE-BSD
@@ -0,0 +1,27 @@
+Copyright (c) 2018, Daniel Wagner-Hall
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of num_enum nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..7a01ae3
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,21 @@
+name: "num_enum_derive"
+description: "Internal implementation details for ::num_enum (Procedural macros to make inter-operation between primitives and enums easier)"
+third_party {
+  identifier {
+    type: "crates.io"
+    value: "num_enum_derive"
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/num_enum_derive/num_enum_derive-0.7.3.crate"
+    primary_source: true
+  }
+  version: "0.7.3"
+  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 8
+    day: 22
+  }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..48bea6e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0191472
--- /dev/null
+++ b/README.md
@@ -0,0 +1,316 @@
+# num_enum
+
+Procedural macros to make inter-operation between primitives and enums easier.
+This crate is no_std compatible.
+
+[![crates.io](https://img.shields.io/crates/v/num_enum.svg)](https://crates.io/crates/num_enum)
+[![Documentation](https://docs.rs/num_enum/badge.svg)](https://docs.rs/num_enum)
+[![Build Status](https://travis-ci.org/illicitonion/num_enum.svg?branch=master)](https://travis-ci.org/illicitonion/num_enum)
+
+## Turning an enum into a primitive
+
+```rust
+use num_enum::IntoPrimitive;
+
+#[derive(IntoPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero,
+    One,
+}
+
+fn main() {
+    let zero: u8 = Number::Zero.into();
+    assert_eq!(zero, 0u8);
+}
+```
+
+`num_enum`'s `IntoPrimitive` is more type-safe than using `as`, because `as` will silently truncate - `num_enum` only derives `From` for exactly the discriminant type of the enum.
+
+## Attempting to turn a primitive into an enum with try_from
+
+```rust
+use num_enum::TryFromPrimitive;
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero,
+    One,
+}
+
+fn main() {
+    let zero = Number::try_from(0u8);
+    assert_eq!(zero, Ok(Number::Zero));
+
+    let three = Number::try_from(3u8);
+    assert_eq!(
+        three.unwrap_err().to_string(),
+        "No discriminant in enum `Number` matches the value `3`",
+    );
+}
+```
+
+### Variant alternatives
+
+Sometimes a single enum variant might be representable by multiple numeric values.
+
+The `#[num_enum(alternatives = [..])]` attribute allows you to define additional value alternatives for individual variants.
+
+(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.)
+
+```rust
+use num_enum::TryFromPrimitive;
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero = 0,
+    #[num_enum(alternatives = [2])]
+    OneOrTwo = 1,
+}
+
+fn main() {
+    let zero = Number::try_from(0u8);
+    assert_eq!(zero, Ok(Number::Zero));
+
+    let one = Number::try_from(1u8);
+    assert_eq!(one, Ok(Number::OneOrTwo));
+
+    let two = Number::try_from(2u8);
+    assert_eq!(two, Ok(Number::OneOrTwo));
+
+    let three = Number::try_from(3u8);
+    assert_eq!(
+        three.unwrap_err().to_string(),
+        "No discriminant in enum `Number` matches the value `3`",
+    );
+}
+```
+
+Range expressions are also supported for alternatives, but this requires enabling the `complex-expressions` feature:
+
+```rust
+use num_enum::TryFromPrimitive;
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero = 0,
+    #[num_enum(alternatives = [2..16])]
+    Some = 1,
+    #[num_enum(alternatives = [17, 18..=255])]
+    Many = 16,
+}
+
+fn main() {
+    let zero = Number::try_from(0u8);
+    assert_eq!(zero, Ok(Number::Zero));
+
+    let some = Number::try_from(15u8);
+    assert_eq!(some, Ok(Number::Some));
+
+    let many = Number::try_from(255u8);
+    assert_eq!(many, Ok(Number::Many));
+}
+```
+
+### Custom error types
+
+`TryFromPrimitive` by default will use `num_enum::TryFromPrimitiveError` as its `Error` type.
+
+If you want to use a different type, you can use an annotation for this:
+
+```rust
+use num_enum::TryFromPrimitive;
+
+#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
+#[repr(u8)]
+enum FirstNumber {
+    Zero,
+    One,
+    Two,
+}
+
+struct CustomError {}
+
+impl CustomError {
+    fn new(value: u8) -> CustomError {
+        CustomError {}
+    }
+}
+```
+
+## Safely turning a primitive into an exhaustive enum with from_primitive
+
+If your enum has all possible primitive values covered, you can derive `FromPrimitive` for it (which auto-implement stdlib's `From`):
+
+You can cover all possible values by:
+* Having variants for every possible value
+* Having a variant marked `#[num_enum(default)]`
+* Having a variant marked `#[num_enum(catch_all)]`
+* Having `#[num_enum(alternatives = [...])`s covering values not covered by a variant.
+
+```rust
+use num_enum::FromPrimitive;
+
+#[derive(Debug, Eq, PartialEq, FromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero,
+    #[num_enum(default)]
+    NonZero,
+}
+
+fn main() {
+    assert_eq!(
+        Number::Zero,
+        Number::from(0_u8),
+    );
+    assert_eq!(
+        Number::NonZero,
+        Number::from(1_u8),
+    );
+}
+```
+
+### Default variant
+
+Sometimes it is desirable to have an `Other` variant in an enum that acts as a kind of a wildcard matching all the value not yet covered by other variants.
+
+The `#[num_enum(default)]` attribute (or the stdlib `#[default]` attribute) allows you to mark variant as the default.
+
+(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.)
+
+```rust
+use num_enum::FromPrimitive;
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, FromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero = 0,
+    #[num_enum(default)]
+    NonZero = 1,
+}
+
+fn main() {
+    let zero = Number::from(0u8);
+    assert_eq!(zero, Number::Zero);
+
+    let one = Number::from(1u8);
+    assert_eq!(one, Number::NonZero);
+
+    let two = Number::from(2u8);
+    assert_eq!(two, Number::NonZero);
+}
+```
+
+Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them.
+
+### Catch-all variant
+
+Sometimes it is desirable to have an `Other` variant which holds the otherwise un-matched value as a field.
+
+The `#[num_enum(catch_all)]` attribute allows you to mark at most one variant for this purpose. The variant it's applied to must be a tuple variant with exactly one field matching the `repr` type.
+
+```rust
+use num_enum::FromPrimitive;
+use std::convert::TryFrom;
+
+#[derive(Debug, Eq, PartialEq, FromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero = 0,
+    #[num_enum(catch_all)]
+    NonZero(u8),
+}
+
+fn main() {
+    let zero = Number::from(0u8);
+    assert_eq!(zero, Number::Zero);
+
+    let one = Number::from(1u8);
+    assert_eq!(one, Number::NonZero(1_u8));
+
+    let two = Number::from(2u8);
+    assert_eq!(two, Number::NonZero(2_u8));
+}
+```
+
+As this is naturally exhaustive, this is only supported for `FromPrimitive`, not also `TryFromPrimitive`.
+
+## Unsafely turning a primitive into an enum with unchecked_transmute_from
+
+If you're really certain a conversion will succeed (and have not made use of `#[num_enum(default)]` or `#[num_enum(alternatives = [..])]`
+for any of its variants), and want to avoid a small amount of overhead, you can use unsafe code to do this conversion.
+Unless you have data showing that the match statement generated in the `try_from` above is a bottleneck for you,
+you should avoid doing this, as the unsafe code has potential to cause serious memory issues in your program.
+
+```rust
+use num_enum::UnsafeFromPrimitive;
+
+#[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero,
+    One,
+}
+
+fn main() {
+    assert_eq!(
+        unsafe { Number::unchecked_transmute_from(0_u8) },
+        Number::Zero,
+    );
+    assert_eq!(
+        unsafe { Number::unchecked_transmute_from(1_u8) },
+        Number::One,
+    );
+}
+
+unsafe fn undefined_behavior() {
+    let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant!
+}
+```
+
+Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
+If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.
+
+This means, for instance, that the following is undefined behaviour:
+
+```rust,no_run
+use num_enum::UnsafeFromPrimitive;
+
+#[derive(UnsafeFromPrimitive)]
+#[repr(u8)]
+enum Number {
+    Zero = 0,
+
+    // Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]`
+    #[num_enum(default)]
+    One = 1,
+}
+let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) };
+```
+
+## Optional features
+
+Some enum values may be composed of complex expressions, for example:
+
+```rust
+enum Number {
+    Zero = (0, 1).0,
+    One = (0, 1).1,
+}
+```
+
+To cut down on compile time, these are not supported by default, but if you enable the `complex-expressions`
+feature of your dependency on `num_enum`, these should start working.
+
+## License
+
+num_enum may be used under your choice of the BSD 3-clause, Apache 2, or MIT license.
diff --git a/src/enum_attributes.rs b/src/enum_attributes.rs
new file mode 100644
index 0000000..fd19a31
--- /dev/null
+++ b/src/enum_attributes.rs
@@ -0,0 +1,274 @@
+use crate::utils::die;
+use proc_macro2::Span;
+use syn::{
+    parse::{Parse, ParseStream},
+    Error, Result,
+};
+
+mod kw {
+    syn::custom_keyword!(constructor);
+    syn::custom_keyword!(error_type);
+    syn::custom_keyword!(name);
+}
+
+// Example: error_type(name = Foo, constructor = Foo::new)
+#[cfg_attr(test, derive(Debug))]
+pub(crate) struct Attributes {
+    pub(crate) error_type: Option<ErrorTypeAttribute>,
+}
+
+// Example: error_type(name = Foo, constructor = Foo::new)
+#[cfg_attr(test, derive(Debug))]
+pub(crate) enum AttributeItem {
+    ErrorType(ErrorTypeAttribute),
+}
+
+impl Parse for Attributes {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let attribute_items = input.parse_terminated(AttributeItem::parse, syn::Token![,])?;
+        let mut maybe_error_type = None;
+        for attribute_item in &attribute_items {
+            match attribute_item {
+                AttributeItem::ErrorType(error_type) => {
+                    if maybe_error_type.is_some() {
+                        return Err(Error::new(
+                            error_type.span,
+                            "num_enum attribute must have at most one error_type",
+                        ));
+                    }
+                    maybe_error_type = Some(error_type.clone());
+                }
+            }
+        }
+        Ok(Self {
+            error_type: maybe_error_type,
+        })
+    }
+}
+
+impl Parse for AttributeItem {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let lookahead = input.lookahead1();
+        if lookahead.peek(kw::error_type) {
+            input.parse().map(Self::ErrorType)
+        } else {
+            Err(lookahead.error())
+        }
+    }
+}
+
+// Example: error_type(name = Foo, constructor = Foo::new)
+#[derive(Clone)]
+#[cfg_attr(test, derive(Debug))]
+pub(crate) struct ErrorTypeAttribute {
+    pub(crate) name: ErrorTypeNameAttribute,
+    pub(crate) constructor: ErrorTypeConstructorAttribute,
+
+    span: Span,
+}
+
+impl Parse for ErrorTypeAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let keyword: kw::error_type = input.parse()?;
+        let span = keyword.span;
+        let content;
+        syn::parenthesized!(content in input);
+        let attribute_values =
+            content.parse_terminated(ErrorTypeAttributeNamedArgument::parse, syn::Token![,])?;
+        let mut name = None;
+        let mut constructor = None;
+        for attribute_value in &attribute_values {
+            match attribute_value {
+                ErrorTypeAttributeNamedArgument::Name(name_attr) => {
+                    if name.is_some() {
+                        die!("num_enum error_type attribute must have exactly one `name` value");
+                    }
+                    name = Some(name_attr.clone());
+                }
+                ErrorTypeAttributeNamedArgument::Constructor(constructor_attr) => {
+                    if constructor.is_some() {
+                        die!("num_enum error_type attribute must have exactly one `constructor` value")
+                    }
+                    constructor = Some(constructor_attr.clone());
+                }
+            }
+        }
+        match (name, constructor) {
+            (None, None) => Err(Error::new(
+                span,
+                "num_enum error_type attribute requires `name` and `constructor` values",
+            )),
+            (Some(_), None) => Err(Error::new(
+                span,
+                "num_enum error_type attribute requires `constructor` value",
+            )),
+            (None, Some(_)) => Err(Error::new(
+                span,
+                "num_enum error_type attribute requires `name` value",
+            )),
+            (Some(name), Some(constructor)) => Ok(Self {
+                name,
+                constructor,
+                span,
+            }),
+        }
+    }
+}
+
+// Examples:
+//  * name = Foo
+//  * constructor = Foo::new
+pub(crate) enum ErrorTypeAttributeNamedArgument {
+    Name(ErrorTypeNameAttribute),
+    Constructor(ErrorTypeConstructorAttribute),
+}
+
+impl Parse for ErrorTypeAttributeNamedArgument {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let lookahead = input.lookahead1();
+        if lookahead.peek(kw::name) {
+            input.parse().map(Self::Name)
+        } else if lookahead.peek(kw::constructor) {
+            input.parse().map(Self::Constructor)
+        } else {
+            Err(lookahead.error())
+        }
+    }
+}
+
+// Example: name = Foo
+#[derive(Clone)]
+#[cfg_attr(test, derive(Debug))]
+pub(crate) struct ErrorTypeNameAttribute {
+    pub(crate) path: syn::Path,
+}
+
+impl Parse for ErrorTypeNameAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        input.parse::<kw::name>()?;
+        input.parse::<syn::Token![=]>()?;
+        let path = input.parse()?;
+        Ok(Self { path })
+    }
+}
+
+// Example: constructor = Foo::new
+#[derive(Clone)]
+#[cfg_attr(test, derive(Debug))]
+pub(crate) struct ErrorTypeConstructorAttribute {
+    pub(crate) path: syn::Path,
+}
+
+impl Parse for ErrorTypeConstructorAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        input.parse::<kw::constructor>()?;
+        input.parse::<syn::Token![=]>()?;
+        let path = input.parse()?;
+        Ok(Self { path })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::enum_attributes::Attributes;
+    use quote::ToTokens;
+    use syn::{parse_quote, Path};
+
+    #[test]
+    fn parse_num_enum_attr() {
+        let expected_name: Path = parse_quote! { Foo };
+        let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new };
+
+        let attributes: Attributes =
+            syn::parse_str("error_type(name = Foo, constructor = ::foo::Foo::<u8>::new)").unwrap();
+        assert!(attributes.error_type.is_some());
+        let error_type = attributes.error_type.unwrap();
+        assert_eq!(
+            error_type.name.path.to_token_stream().to_string(),
+            expected_name.to_token_stream().to_string()
+        );
+        assert_eq!(
+            error_type.constructor.path.to_token_stream().to_string(),
+            expected_constructor.to_token_stream().to_string()
+        );
+    }
+
+    #[test]
+    fn parse_num_enum_attr_swapped_order() {
+        let expected_name: Path = parse_quote! { Foo };
+        let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new };
+
+        let attributes: Attributes =
+            syn::parse_str("error_type(constructor = ::foo::Foo::<u8>::new, name = Foo)").unwrap();
+        assert!(attributes.error_type.is_some());
+        let error_type = attributes.error_type.unwrap();
+        assert_eq!(
+            error_type.name.path.to_token_stream().to_string(),
+            expected_name.to_token_stream().to_string()
+        );
+        assert_eq!(
+            error_type.constructor.path.to_token_stream().to_string(),
+            expected_constructor.to_token_stream().to_string()
+        );
+    }
+
+    #[test]
+    fn missing_constructor() {
+        let err = syn::parse_str::<Attributes>("error_type(name = Foo)").unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "num_enum error_type attribute requires `constructor` value"
+        );
+    }
+
+    #[test]
+    fn missing_name() {
+        let err = syn::parse_str::<Attributes>("error_type(constructor = Foo::new)").unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "num_enum error_type attribute requires `name` value"
+        );
+    }
+
+    #[test]
+    fn missing_both() {
+        let err = syn::parse_str::<Attributes>("error_type()").unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "num_enum error_type attribute requires `name` and `constructor` values"
+        );
+    }
+
+    #[test]
+    fn extra_attr() {
+        let err = syn::parse_str::<Attributes>(
+            "error_type(name = Foo, constructor = Foo::new, extra = unneeded)",
+        )
+        .unwrap_err();
+        assert_eq!(err.to_string(), "expected `name` or `constructor`");
+    }
+
+    #[test]
+    fn multiple_names() {
+        let err = syn::parse_str::<Attributes>(
+            "error_type(name = Foo, name = Foo, constructor = Foo::new)",
+        )
+        .unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "num_enum error_type attribute must have exactly one `name` value"
+        );
+    }
+
+    #[test]
+    fn multiple_constructors() {
+        let err = syn::parse_str::<Attributes>(
+            "error_type(name = Foo, constructor = Foo::new, constructor = Foo::new)",
+        )
+        .unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "num_enum error_type attribute must have exactly one `constructor` value"
+        );
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..4fc4927
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,363 @@
+// Not supported by MSRV
+#![allow(clippy::uninlined_format_args)]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{parse_macro_input, Expr, Ident};
+
+mod enum_attributes;
+mod parsing;
+use parsing::{get_crate_name, EnumInfo};
+mod utils;
+mod variant_attributes;
+
+/// Implements `Into<Primitive>` for a `#[repr(Primitive)] enum`.
+///
+/// (It actually implements `From<Enum> for Primitive`)
+///
+/// ## Allows turning an enum into a primitive.
+///
+/// ```rust
+/// use num_enum::IntoPrimitive;
+///
+/// #[derive(IntoPrimitive)]
+/// #[repr(u8)]
+/// enum Number {
+///     Zero,
+///     One,
+/// }
+///
+/// let zero: u8 = Number::Zero.into();
+/// assert_eq!(zero, 0u8);
+/// ```
+#[proc_macro_derive(IntoPrimitive, attributes(num_enum, catch_all))]
+pub fn derive_into_primitive(input: TokenStream) -> TokenStream {
+    let enum_info = parse_macro_input!(input as EnumInfo);
+    let catch_all = enum_info.catch_all();
+    let name = &enum_info.name;
+    let repr = &enum_info.repr;
+
+    let body = if let Some(catch_all_ident) = catch_all {
+        quote! {
+            match enum_value {
+                #name::#catch_all_ident(raw) => raw,
+                rest => unsafe { *(&rest as *const #name as *const Self) }
+            }
+        }
+    } else {
+        quote! { enum_value as Self }
+    };
+
+    TokenStream::from(quote! {
+        impl From<#name> for #repr {
+            #[inline]
+            fn from (enum_value: #name) -> Self
+            {
+                #body
+            }
+        }
+    })
+}
+
+/// Implements `From<Primitive>` for a `#[repr(Primitive)] enum`.
+///
+/// Turning a primitive into an enum with `from`.
+/// ----------------------------------------------
+///
+/// ```rust
+/// use num_enum::FromPrimitive;
+///
+/// #[derive(Debug, Eq, PartialEq, FromPrimitive)]
+/// #[repr(u8)]
+/// enum Number {
+///     Zero,
+///     #[num_enum(default)]
+///     NonZero,
+/// }
+///
+/// let zero = Number::from(0u8);
+/// assert_eq!(zero, Number::Zero);
+///
+/// let one = Number::from(1u8);
+/// assert_eq!(one, Number::NonZero);
+///
+/// let two = Number::from(2u8);
+/// assert_eq!(two, Number::NonZero);
+/// ```
+#[proc_macro_derive(FromPrimitive, attributes(num_enum, default, catch_all))]
+pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
+    let enum_info: EnumInfo = parse_macro_input!(input);
+    let krate = Ident::new(&get_crate_name(), Span::call_site());
+
+    let is_naturally_exhaustive = enum_info.is_naturally_exhaustive();
+    let catch_all_body = match is_naturally_exhaustive {
+        Ok(is_naturally_exhaustive) => {
+            if is_naturally_exhaustive {
+                quote! { unreachable!("exhaustive enum") }
+            } else if let Some(default_ident) = enum_info.default() {
+                quote! { Self::#default_ident }
+            } else if let Some(catch_all_ident) = enum_info.catch_all() {
+                quote! { Self::#catch_all_ident(number) }
+            } else {
+                let span = Span::call_site();
+                let message =
+                    "#[derive(num_enum::FromPrimitive)] requires enum to be exhaustive, or a variant marked with `#[default]`, `#[num_enum(default)]`, or `#[num_enum(catch_all)`";
+                return syn::Error::new(span, message).to_compile_error().into();
+            }
+        }
+        Err(err) => {
+            return err.to_compile_error().into();
+        }
+    };
+
+    let EnumInfo {
+        ref name, ref repr, ..
+    } = enum_info;
+
+    let variant_idents: Vec<Ident> = enum_info.variant_idents();
+    let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents();
+    let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions();
+
+    debug_assert_eq!(variant_idents.len(), variant_expressions.len());
+
+    TokenStream::from(quote! {
+        impl ::#krate::FromPrimitive for #name {
+            type Primitive = #repr;
+
+            fn from_primitive(number: Self::Primitive) -> Self {
+                // Use intermediate const(s) so that enums defined like
+                // `Two = ONE + 1u8` work properly.
+                #![allow(non_upper_case_globals)]
+                #(
+                    #(
+                        const #expression_idents: #repr = #variant_expressions;
+                    )*
+                )*
+                #[deny(unreachable_patterns)]
+                match number {
+                    #(
+                        #( #expression_idents )|*
+                        => Self::#variant_idents,
+                    )*
+                    #[allow(unreachable_patterns)]
+                    _ => #catch_all_body,
+                }
+            }
+        }
+
+        impl ::core::convert::From<#repr> for #name {
+            #[inline]
+            fn from (
+                number: #repr,
+            ) -> Self {
+                ::#krate::FromPrimitive::from_primitive(number)
+            }
+        }
+
+        #[doc(hidden)]
+        impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
+    })
+}
+
+/// Implements `TryFrom<Primitive>` for a `#[repr(Primitive)] enum`.
+///
+/// Attempting to turn a primitive into an enum with `try_from`.
+/// ----------------------------------------------
+///
+/// ```rust
+/// use num_enum::TryFromPrimitive;
+/// use std::convert::TryFrom;
+///
+/// #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
+/// #[repr(u8)]
+/// enum Number {
+///     Zero,
+///     One,
+/// }
+///
+/// let zero = Number::try_from(0u8);
+/// assert_eq!(zero, Ok(Number::Zero));
+///
+/// let three = Number::try_from(3u8);
+/// assert_eq!(
+///     three.unwrap_err().to_string(),
+///     "No discriminant in enum `Number` matches the value `3`",
+/// );
+/// ```
+#[proc_macro_derive(TryFromPrimitive, attributes(num_enum))]
+pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
+    let enum_info: EnumInfo = parse_macro_input!(input);
+    let krate = Ident::new(&get_crate_name(), Span::call_site());
+
+    let EnumInfo {
+        ref name,
+        ref repr,
+        ref error_type_info,
+        ..
+    } = enum_info;
+
+    let variant_idents: Vec<Ident> = enum_info.variant_idents();
+    let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents();
+    let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions();
+
+    debug_assert_eq!(variant_idents.len(), variant_expressions.len());
+
+    let error_type = &error_type_info.name;
+    let error_constructor = &error_type_info.constructor;
+
+    TokenStream::from(quote! {
+        impl ::#krate::TryFromPrimitive for #name {
+            type Primitive = #repr;
+            type Error = #error_type;
+
+            const NAME: &'static str = stringify!(#name);
+
+            fn try_from_primitive (
+                number: Self::Primitive,
+            ) -> ::core::result::Result<
+                Self,
+                #error_type
+            > {
+                // Use intermediate const(s) so that enums defined like
+                // `Two = ONE + 1u8` work properly.
+                #![allow(non_upper_case_globals)]
+                #(
+                    #(
+                        const #expression_idents: #repr = #variant_expressions;
+                    )*
+                )*
+                #[deny(unreachable_patterns)]
+                match number {
+                    #(
+                        #( #expression_idents )|*
+                        => ::core::result::Result::Ok(Self::#variant_idents),
+                    )*
+                    #[allow(unreachable_patterns)]
+                    _ => ::core::result::Result::Err(
+                        #error_constructor ( number )
+                    ),
+                }
+            }
+        }
+
+        impl ::core::convert::TryFrom<#repr> for #name {
+            type Error = #error_type;
+
+            #[inline]
+            fn try_from (
+                number: #repr,
+            ) -> ::core::result::Result<Self, #error_type>
+            {
+                ::#krate::TryFromPrimitive::try_from_primitive(number)
+            }
+        }
+
+        #[doc(hidden)]
+        impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
+    })
+}
+
+/// Generates a `unsafe fn unchecked_transmute_from(number: Primitive) -> Self`
+/// associated function.
+///
+/// Allows unsafely turning a primitive into an enum with unchecked_transmute_from
+/// ------------------------------------------------------------------------------
+///
+/// If you're really certain a conversion will succeed, and want to avoid a small amount of overhead, you can use unsafe
+/// code to do this conversion. Unless you have data showing that the match statement generated in the `try_from` above is a
+/// bottleneck for you, you should avoid doing this, as the unsafe code has potential to cause serious memory issues in
+/// your program.
+///
+/// Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
+/// If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.
+///
+/// ```rust
+/// use num_enum::UnsafeFromPrimitive;
+///
+/// #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
+/// #[repr(u8)]
+/// enum Number {
+///     Zero,
+///     One,
+/// }
+///
+/// fn main() {
+///     assert_eq!(
+///         Number::Zero,
+///         unsafe { Number::unchecked_transmute_from(0_u8) },
+///     );
+///     assert_eq!(
+///         Number::One,
+///         unsafe { Number::unchecked_transmute_from(1_u8) },
+///     );
+/// }
+///
+/// unsafe fn undefined_behavior() {
+///     let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant!
+/// }
+/// ```
+#[proc_macro_derive(UnsafeFromPrimitive, attributes(num_enum))]
+pub fn derive_unsafe_from_primitive(stream: TokenStream) -> TokenStream {
+    let enum_info = parse_macro_input!(stream as EnumInfo);
+    let krate = Ident::new(&get_crate_name(), Span::call_site());
+
+    let EnumInfo {
+        ref name, ref repr, ..
+    } = enum_info;
+
+    TokenStream::from(quote! {
+        impl ::#krate::UnsafeFromPrimitive for #name {
+            type Primitive = #repr;
+
+            unsafe fn unchecked_transmute_from(number: Self::Primitive) -> Self {
+                ::core::mem::transmute(number)
+            }
+        }
+    })
+}
+
+/// Implements `core::default::Default` for a `#[repr(Primitive)] enum`.
+///
+/// Whichever variant has the `#[default]` or `#[num_enum(default)]` attribute will be returned.
+/// ----------------------------------------------
+///
+/// ```rust
+/// #[derive(Debug, Eq, PartialEq, num_enum::Default)]
+/// #[repr(u8)]
+/// enum Number {
+///     Zero,
+///     #[default]
+///     One,
+/// }
+///
+/// assert_eq!(Number::One, Number::default());
+/// assert_eq!(Number::One, <Number as ::core::default::Default>::default());
+/// ```
+#[proc_macro_derive(Default, attributes(num_enum, default))]
+pub fn derive_default(stream: TokenStream) -> TokenStream {
+    let enum_info = parse_macro_input!(stream as EnumInfo);
+
+    let default_ident = match enum_info.default() {
+        Some(ident) => ident,
+        None => {
+            let span = Span::call_site();
+            let message =
+                "#[derive(num_enum::Default)] requires enum to be exhaustive, or a variant marked with `#[default]` or `#[num_enum(default)]`";
+            return syn::Error::new(span, message).to_compile_error().into();
+        }
+    };
+
+    let EnumInfo { ref name, .. } = enum_info;
+
+    TokenStream::from(quote! {
+        impl ::core::default::Default for #name {
+            #[inline]
+            fn default() -> Self {
+                Self::#default_ident
+            }
+        }
+    })
+}
diff --git a/src/parsing.rs b/src/parsing.rs
new file mode 100644
index 0000000..1a24b0c
--- /dev/null
+++ b/src/parsing.rs
@@ -0,0 +1,554 @@
+use crate::enum_attributes::ErrorTypeAttribute;
+use crate::utils::die;
+use crate::variant_attributes::{NumEnumVariantAttributeItem, NumEnumVariantAttributes};
+use proc_macro2::Span;
+use quote::{format_ident, ToTokens};
+use std::collections::BTreeSet;
+use syn::{
+    parse::{Parse, ParseStream},
+    parse_quote, Attribute, Data, DeriveInput, Expr, ExprLit, ExprUnary, Fields, Ident, Lit,
+    LitInt, Meta, Path, Result, UnOp,
+};
+
+pub(crate) struct EnumInfo {
+    pub(crate) name: Ident,
+    pub(crate) repr: Ident,
+    pub(crate) variants: Vec<VariantInfo>,
+    pub(crate) error_type_info: ErrorType,
+}
+
+impl EnumInfo {
+    /// Returns whether the number of variants (ignoring defaults, catch-alls, etc) is the same as
+    /// the capacity of the repr.
+    pub(crate) fn is_naturally_exhaustive(&self) -> Result<bool> {
+        let repr_str = self.repr.to_string();
+        if !repr_str.is_empty() {
+            let suffix = repr_str
+                .strip_prefix('i')
+                .or_else(|| repr_str.strip_prefix('u'));
+            if let Some(suffix) = suffix {
+                if suffix == "size" {
+                    return Ok(false);
+                } else if let Ok(bits) = suffix.parse::<u32>() {
+                    let variants = 1usize.checked_shl(bits);
+                    return Ok(variants.map_or(false, |v| {
+                        v == self
+                            .variants
+                            .iter()
+                            .map(|v| v.alternative_values.len() + 1)
+                            .sum()
+                    }));
+                }
+            }
+        }
+        die!(self.repr.clone() => "Failed to parse repr into bit size");
+    }
+
+    pub(crate) fn default(&self) -> Option<&Ident> {
+        self.variants
+            .iter()
+            .find(|info| info.is_default)
+            .map(|info| &info.ident)
+    }
+
+    pub(crate) fn catch_all(&self) -> Option<&Ident> {
+        self.variants
+            .iter()
+            .find(|info| info.is_catch_all)
+            .map(|info| &info.ident)
+    }
+
+    pub(crate) fn variant_idents(&self) -> Vec<Ident> {
+        self.variants
+            .iter()
+            .filter(|variant| !variant.is_catch_all)
+            .map(|variant| variant.ident.clone())
+            .collect()
+    }
+
+    pub(crate) fn expression_idents(&self) -> Vec<Vec<Ident>> {
+        self.variants
+            .iter()
+            .filter(|variant| !variant.is_catch_all)
+            .map(|info| {
+                let indices = 0..(info.alternative_values.len() + 1);
+                indices
+                    .map(|index| format_ident!("{}__num_enum_{}__", info.ident, index))
+                    .collect()
+            })
+            .collect()
+    }
+
+    pub(crate) fn variant_expressions(&self) -> Vec<Vec<Expr>> {
+        self.variants
+            .iter()
+            .filter(|variant| !variant.is_catch_all)
+            .map(|variant| variant.all_values().cloned().collect())
+            .collect()
+    }
+
+    fn parse_attrs<Attrs: Iterator<Item = Attribute>>(
+        attrs: Attrs,
+    ) -> Result<(Ident, Option<ErrorType>)> {
+        let mut maybe_repr = None;
+        let mut maybe_error_type = None;
+        for attr in attrs {
+            if let Meta::List(meta_list) = &attr.meta {
+                if let Some(ident) = meta_list.path.get_ident() {
+                    if ident == "repr" {
+                        let mut nested = meta_list.tokens.clone().into_iter();
+                        let repr_tree = match (nested.next(), nested.next()) {
+                            (Some(repr_tree), None) => repr_tree,
+                            _ => die!(attr =>
+                                "Expected exactly one `repr` argument"
+                            ),
+                        };
+                        let repr_ident: Ident = parse_quote! {
+                            #repr_tree
+                        };
+                        if repr_ident == "C" {
+                            die!(repr_ident =>
+                                "repr(C) doesn't have a well defined size"
+                            );
+                        } else {
+                            maybe_repr = Some(repr_ident);
+                        }
+                    } else if ident == "num_enum" {
+                        let attributes =
+                            attr.parse_args_with(crate::enum_attributes::Attributes::parse)?;
+                        if let Some(error_type) = attributes.error_type {
+                            if maybe_error_type.is_some() {
+                                die!(attr => "At most one num_enum error_type attribute may be specified");
+                            }
+                            maybe_error_type = Some(error_type.into());
+                        }
+                    }
+                }
+            }
+        }
+        if maybe_repr.is_none() {
+            die!("Missing `#[repr({Integer})]` attribute");
+        }
+        Ok((maybe_repr.unwrap(), maybe_error_type))
+    }
+}
+
+impl Parse for EnumInfo {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok({
+            let input: DeriveInput = input.parse()?;
+            let name = input.ident;
+            let data = match input.data {
+                Data::Enum(data) => data,
+                Data::Union(data) => die!(data.union_token => "Expected enum but found union"),
+                Data::Struct(data) => die!(data.struct_token => "Expected enum but found struct"),
+            };
+
+            let (repr, maybe_error_type) = Self::parse_attrs(input.attrs.into_iter())?;
+
+            let mut variants: Vec<VariantInfo> = vec![];
+            let mut has_default_variant: bool = false;
+            let mut has_catch_all_variant: bool = false;
+
+            // Vec to keep track of the used discriminants and alt values.
+            let mut discriminant_int_val_set = BTreeSet::new();
+
+            let mut next_discriminant = literal(0);
+            for variant in data.variants.into_iter() {
+                let ident = variant.ident.clone();
+
+                let discriminant = match &variant.discriminant {
+                    Some(d) => d.1.clone(),
+                    None => next_discriminant.clone(),
+                };
+
+                let mut raw_alternative_values: Vec<Expr> = vec![];
+                // Keep the attribute around for better error reporting.
+                let mut alt_attr_ref: Vec<&Attribute> = vec![];
+
+                // `#[num_enum(default)]` is required by `#[derive(FromPrimitive)]`
+                // and forbidden by `#[derive(UnsafeFromPrimitive)]`, so we need to
+                // keep track of whether we encountered such an attribute:
+                let mut is_default: bool = false;
+                let mut is_catch_all: bool = false;
+
+                for attribute in &variant.attrs {
+                    if attribute.path().is_ident("default") {
+                        if has_default_variant {
+                            die!(attribute =>
+                                "Multiple variants marked `#[default]` or `#[num_enum(default)]` found"
+                            );
+                        } else if has_catch_all_variant {
+                            die!(attribute =>
+                                "Attribute `default` is mutually exclusive with `catch_all`"
+                            );
+                        }
+                        is_default = true;
+                        has_default_variant = true;
+                    }
+
+                    if attribute.path().is_ident("num_enum") {
+                        match attribute.parse_args_with(NumEnumVariantAttributes::parse) {
+                            Ok(variant_attributes) => {
+                                for variant_attribute in variant_attributes.items {
+                                    match variant_attribute {
+                                        NumEnumVariantAttributeItem::Default(default) => {
+                                            if has_default_variant {
+                                                die!(default.keyword =>
+                                                    "Multiple variants marked `#[default]` or `#[num_enum(default)]` found"
+                                                );
+                                            } else if has_catch_all_variant {
+                                                die!(default.keyword =>
+                                                    "Attribute `default` is mutually exclusive with `catch_all`"
+                                                );
+                                            }
+                                            is_default = true;
+                                            has_default_variant = true;
+                                        }
+                                        NumEnumVariantAttributeItem::CatchAll(catch_all) => {
+                                            if has_catch_all_variant {
+                                                die!(catch_all.keyword =>
+                                                    "Multiple variants marked with `#[num_enum(catch_all)]`"
+                                                );
+                                            } else if has_default_variant {
+                                                die!(catch_all.keyword =>
+                                                    "Attribute `catch_all` is mutually exclusive with `default`"
+                                                );
+                                            }
+
+                                            match variant
+                                                .fields
+                                                .iter()
+                                                .collect::<Vec<_>>()
+                                                .as_slice()
+                                            {
+                                                [syn::Field {
+                                                    ty: syn::Type::Path(syn::TypePath { path, .. }),
+                                                    ..
+                                                }] if path.is_ident(&repr) => {
+                                                    is_catch_all = true;
+                                                    has_catch_all_variant = true;
+                                                }
+                                                _ => {
+                                                    die!(catch_all.keyword =>
+                                                        "Variant with `catch_all` must be a tuple with exactly 1 field matching the repr type"
+                                                    );
+                                                }
+                                            }
+                                        }
+                                        NumEnumVariantAttributeItem::Alternatives(alternatives) => {
+                                            raw_alternative_values.extend(alternatives.expressions);
+                                            alt_attr_ref.push(attribute);
+                                        }
+                                    }
+                                }
+                            }
+                            Err(err) => {
+                                if cfg!(not(feature = "complex-expressions")) {
+                                    let tokens = attribute.meta.to_token_stream();
+
+                                    let attribute_str = format!("{}", tokens);
+                                    if attribute_str.contains("alternatives")
+                                        && attribute_str.contains("..")
+                                    {
+                                        // Give a nice error message suggesting how to fix the problem.
+                                        die!(attribute => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string())
+                                    }
+                                }
+                                die!(attribute =>
+                                    format!("Invalid attribute: {}", err)
+                                );
+                            }
+                        }
+                    }
+                }
+
+                if !is_catch_all {
+                    match &variant.fields {
+                        Fields::Named(_) | Fields::Unnamed(_) => {
+                            die!(variant => format!("`{}` only supports unit variants (with no associated data), but `{}::{}` was not a unit variant.", get_crate_name(), name, ident));
+                        }
+                        Fields::Unit => {}
+                    }
+                }
+
+                let discriminant_value = parse_discriminant(&discriminant)?;
+
+                // Check for collision.
+                // We can't do const evaluation, or even compare arbitrary Exprs,
+                // so unfortunately we can't check for duplicates.
+                // That's not the end of the world, just we'll end up with compile errors for
+                // matches with duplicate branches in generated code instead of nice friendly error messages.
+                if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
+                    if discriminant_int_val_set.contains(&canonical_value_int) {
+                        die!(ident => format!("The discriminant '{}' collides with a value attributed to a previous variant", canonical_value_int))
+                    }
+                }
+
+                // Deal with the alternative values.
+                let mut flattened_alternative_values = Vec::new();
+                let mut flattened_raw_alternative_values = Vec::new();
+                for raw_alternative_value in raw_alternative_values {
+                    let expanded_values = parse_alternative_values(&raw_alternative_value)?;
+                    for expanded_value in expanded_values {
+                        flattened_alternative_values.push(expanded_value);
+                        flattened_raw_alternative_values.push(raw_alternative_value.clone())
+                    }
+                }
+
+                if !flattened_alternative_values.is_empty() {
+                    let alternate_int_values = flattened_alternative_values
+                        .into_iter()
+                        .map(|v| {
+                            match v {
+                                DiscriminantValue::Literal(value) => Ok(value),
+                                DiscriminantValue::Expr(expr) => {
+                                    if let Expr::Range(_) = expr {
+                                        if cfg!(not(feature = "complex-expressions")) {
+                                            // Give a nice error message suggesting how to fix the problem.
+                                            die!(expr => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string())
+                                        }
+                                    }
+                                    // We can't do uniqueness checking on non-literals, so we don't allow them as alternate values.
+                                    // We could probably allow them, but there doesn't seem to be much of a use-case,
+                                    // and it's easier to give good error messages about duplicate values this way,
+                                    // rather than rustc errors on conflicting match branches.
+                                    die!(expr => "Only literals are allowed as num_enum alternate values".to_string())
+                                },
+                            }
+                        })
+                        .collect::<Result<Vec<i128>>>()?;
+                    let mut sorted_alternate_int_values = alternate_int_values.clone();
+                    sorted_alternate_int_values.sort_unstable();
+                    let sorted_alternate_int_values = sorted_alternate_int_values;
+
+                    // Check if the current discriminant is not in the alternative values.
+                    if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
+                        if let Some(index) = alternate_int_values
+                            .iter()
+                            .position(|&x| x == canonical_value_int)
+                        {
+                            die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed as the discriminant of this variant", canonical_value_int));
+                        }
+                    }
+
+                    // Search for duplicates, the vec is sorted. Warn about them.
+                    if (1..sorted_alternate_int_values.len()).any(|i| {
+                        sorted_alternate_int_values[i] == sorted_alternate_int_values[i - 1]
+                    }) {
+                        let attr = *alt_attr_ref.last().unwrap();
+                        die!(attr => "There is duplication in the alternative values");
+                    }
+                    // Search if those discriminant_int_val_set where already attributed.
+                    // (discriminant_int_val_set is BTreeSet, and iter().next_back() is the is the maximum in the set.)
+                    if let Some(last_upper_val) = discriminant_int_val_set.iter().next_back() {
+                        if sorted_alternate_int_values.first().unwrap() <= last_upper_val {
+                            for (index, val) in alternate_int_values.iter().enumerate() {
+                                if discriminant_int_val_set.contains(val) {
+                                    die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed to a previous variant", val));
+                                }
+                            }
+                        }
+                    }
+
+                    // Reconstruct the alternative_values vec of Expr but sorted.
+                    flattened_raw_alternative_values = sorted_alternate_int_values
+                        .iter()
+                        .map(|val| literal(val.to_owned()))
+                        .collect();
+
+                    // Add the alternative values to the the set to keep track.
+                    discriminant_int_val_set.extend(sorted_alternate_int_values);
+                }
+
+                // Add the current discriminant to the the set to keep track.
+                if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value {
+                    discriminant_int_val_set.insert(canonical_value_int);
+                }
+
+                variants.push(VariantInfo {
+                    ident,
+                    is_default,
+                    is_catch_all,
+                    canonical_value: discriminant,
+                    alternative_values: flattened_raw_alternative_values,
+                });
+
+                // Get the next value for the discriminant.
+                next_discriminant = match discriminant_value {
+                    DiscriminantValue::Literal(int_value) => literal(int_value.wrapping_add(1)),
+                    DiscriminantValue::Expr(expr) => {
+                        parse_quote! {
+                            #repr::wrapping_add(#expr, 1)
+                        }
+                    }
+                }
+            }
+
+            let error_type_info = maybe_error_type.unwrap_or_else(|| {
+                let crate_name = Ident::new(&get_crate_name(), Span::call_site());
+                ErrorType {
+                    name: parse_quote! {
+                        ::#crate_name::TryFromPrimitiveError<Self>
+                    },
+                    constructor: parse_quote! {
+                        ::#crate_name::TryFromPrimitiveError::<Self>::new
+                    },
+                }
+            });
+
+            EnumInfo {
+                name,
+                repr,
+                variants,
+                error_type_info,
+            }
+        })
+    }
+}
+
+fn literal(i: i128) -> Expr {
+    Expr::Lit(ExprLit {
+        lit: Lit::Int(LitInt::new(&i.to_string(), Span::call_site())),
+        attrs: vec![],
+    })
+}
+
+enum DiscriminantValue {
+    Literal(i128),
+    Expr(Expr),
+}
+
+fn parse_discriminant(val_exp: &Expr) -> Result<DiscriminantValue> {
+    let mut sign = 1;
+    let mut unsigned_expr = val_exp;
+    if let Expr::Unary(ExprUnary {
+        op: UnOp::Neg(..),
+        expr,
+        ..
+    }) = val_exp
+    {
+        unsigned_expr = expr;
+        sign = -1;
+    }
+    if let Expr::Lit(ExprLit {
+        lit: Lit::Int(ref lit_int),
+        ..
+    }) = unsigned_expr
+    {
+        Ok(DiscriminantValue::Literal(
+            sign * lit_int.base10_parse::<i128>()?,
+        ))
+    } else {
+        Ok(DiscriminantValue::Expr(val_exp.clone()))
+    }
+}
+
+#[cfg(feature = "complex-expressions")]
+fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> {
+    fn range_expr_value_to_number(
+        parent_range_expr: &Expr,
+        range_bound_value: &Option<Box<Expr>>,
+    ) -> Result<i128> {
+        // Avoid needing to calculate what the lower and upper bound would be - these are type dependent,
+        // and also may not be obvious in context (e.g. an omitted bound could reasonably mean "from the last discriminant" or "from the lower bound of the type").
+        if let Some(range_bound_value) = range_bound_value {
+            let range_bound_value = parse_discriminant(range_bound_value.as_ref())?;
+            // If non-literals are used, we can't expand to the mapped values, so can't write a nice match statement or do exhaustiveness checking.
+            // Require literals instead.
+            if let DiscriminantValue::Literal(value) = range_bound_value {
+                return Ok(value);
+            }
+        }
+        die!(parent_range_expr => "When ranges are used for alternate values, both bounds most be explicitly specified numeric literals")
+    }
+
+    if let Expr::Range(syn::ExprRange {
+        start, end, limits, ..
+    }) = val_expr
+    {
+        let lower = range_expr_value_to_number(val_expr, start)?;
+        let upper = range_expr_value_to_number(val_expr, end)?;
+        // While this is technically allowed in Rust, and results in an empty range, it's almost certainly a mistake in this context.
+        if lower > upper {
+            die!(val_expr => "When using ranges for alternate values, upper bound must not be less than lower bound");
+        }
+        let mut values = Vec::with_capacity((upper - lower) as usize);
+        let mut next = lower;
+        loop {
+            match limits {
+                syn::RangeLimits::HalfOpen(..) => {
+                    if next == upper {
+                        break;
+                    }
+                }
+                syn::RangeLimits::Closed(..) => {
+                    if next > upper {
+                        break;
+                    }
+                }
+            }
+            values.push(DiscriminantValue::Literal(next));
+            next += 1;
+        }
+        return Ok(values);
+    }
+    parse_discriminant(val_expr).map(|v| vec![v])
+}
+
+#[cfg(not(feature = "complex-expressions"))]
+fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> {
+    parse_discriminant(val_expr).map(|v| vec![v])
+}
+
+pub(crate) struct VariantInfo {
+    ident: Ident,
+    is_default: bool,
+    is_catch_all: bool,
+    canonical_value: Expr,
+    alternative_values: Vec<Expr>,
+}
+
+impl VariantInfo {
+    fn all_values(&self) -> impl Iterator<Item = &Expr> {
+        ::core::iter::once(&self.canonical_value).chain(self.alternative_values.iter())
+    }
+}
+
+pub(crate) struct ErrorType {
+    pub(crate) name: Path,
+    pub(crate) constructor: Path,
+}
+
+impl From<ErrorTypeAttribute> for ErrorType {
+    fn from(attribute: ErrorTypeAttribute) -> Self {
+        Self {
+            name: attribute.name.path,
+            constructor: attribute.constructor.path,
+        }
+    }
+}
+
+#[cfg(feature = "proc-macro-crate")]
+pub(crate) fn get_crate_name() -> String {
+    let found_crate = proc_macro_crate::crate_name("num_enum").unwrap_or_else(|err| {
+        eprintln!("Warning: {}\n    => defaulting to `num_enum`", err,);
+        proc_macro_crate::FoundCrate::Itself
+    });
+
+    match found_crate {
+        proc_macro_crate::FoundCrate::Itself => String::from("num_enum"),
+        proc_macro_crate::FoundCrate::Name(name) => name,
+    }
+}
+
+// Don't depend on proc-macro-crate in no_std environments because it causes an awkward dependency
+// on serde with std.
+//
+// no_std dependees on num_enum cannot rename the num_enum crate when they depend on it. Sorry.
+//
+// See https://github.com/illicitonion/num_enum/issues/18
+#[cfg(not(feature = "proc-macro-crate"))]
+pub(crate) fn get_crate_name() -> String {
+    String::from("num_enum")
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..ed6ab87
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,15 @@
+macro_rules! die {
+    ($spanned:expr=>
+        $msg:expr
+    ) => {
+        return Err(::syn::Error::new_spanned($spanned, $msg))
+    };
+
+    (
+        $msg:expr
+    ) => {
+        return Err(::syn::Error::new(::proc_macro2::Span::call_site(), $msg))
+    };
+}
+
+pub(crate) use die;
diff --git a/src/variant_attributes.rs b/src/variant_attributes.rs
new file mode 100644
index 0000000..3b58a22
--- /dev/null
+++ b/src/variant_attributes.rs
@@ -0,0 +1,90 @@
+use syn::{
+    parse::{Parse, ParseStream},
+    Expr, Result,
+};
+
+mod kw {
+    syn::custom_keyword!(default);
+    syn::custom_keyword!(catch_all);
+    syn::custom_keyword!(alternatives);
+}
+
+pub(crate) struct NumEnumVariantAttributes {
+    pub(crate) items: syn::punctuated::Punctuated<NumEnumVariantAttributeItem, syn::Token![,]>,
+}
+
+impl Parse for NumEnumVariantAttributes {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        Ok(Self {
+            items: input.parse_terminated(NumEnumVariantAttributeItem::parse, syn::Token![,])?,
+        })
+    }
+}
+
+pub(crate) enum NumEnumVariantAttributeItem {
+    Default(VariantDefaultAttribute),
+    CatchAll(VariantCatchAllAttribute),
+    Alternatives(VariantAlternativesAttribute),
+}
+
+impl Parse for NumEnumVariantAttributeItem {
+    fn parse(input: ParseStream<'_>) -> Result<Self> {
+        let lookahead = input.lookahead1();
+        if lookahead.peek(kw::default) {
+            input.parse().map(Self::Default)
+        } else if lookahead.peek(kw::catch_all) {
+            input.parse().map(Self::CatchAll)
+        } else if lookahead.peek(kw::alternatives) {
+            input.parse().map(Self::Alternatives)
+        } else {
+            Err(lookahead.error())
+        }
+    }
+}
+
+pub(crate) struct VariantDefaultAttribute {
+    pub(crate) keyword: kw::default,
+}
+
+impl Parse for VariantDefaultAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok(Self {
+            keyword: input.parse()?,
+        })
+    }
+}
+
+pub(crate) struct VariantCatchAllAttribute {
+    pub(crate) keyword: kw::catch_all,
+}
+
+impl Parse for VariantCatchAllAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        Ok(Self {
+            keyword: input.parse()?,
+        })
+    }
+}
+
+pub(crate) struct VariantAlternativesAttribute {
+    _keyword: kw::alternatives,
+    _eq_token: syn::Token![=],
+    _bracket_token: syn::token::Bracket,
+    pub(crate) expressions: syn::punctuated::Punctuated<Expr, syn::Token![,]>,
+}
+
+impl Parse for VariantAlternativesAttribute {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let content;
+        let keyword = input.parse()?;
+        let _eq_token = input.parse()?;
+        let _bracket_token = syn::bracketed!(content in input);
+        let expressions = content.parse_terminated(Expr::parse, syn::Token![,])?;
+        Ok(Self {
+            _keyword: keyword,
+            _eq_token,
+            _bracket_token,
+            expressions,
+        })
+    }
+}