# nested-deserialize
A very special-case implementation of `serde::Deserialize` for untagged nested enums (also known as _merged enums_ or _enum unions_).
It applies to the following case:
* "Outer" enum is untagged
* Outer enum has 1-length tuple variants
* ..of which the value is an enum
* which _should_ not have colliding variant names with its "sibling enums" used in other variants of the outer enum (also known as the term _non-overlapping_)
* which must implement `serde::Deserialize`
* specifically as an [externally tagged enum](https://serde.rs/enum-representations.html#externally-tagged)
* and [`strum::VariantNames`](https://docs.rs/strum/latest/strum/derive.VariantNames.html)
* where the renaming rules (if any) _must_ be in sync with serde
It effectively achieves the same as copy-pasting the variants of each inner enum into an outer enum (or using a macro to do this), and then implementing `TryFrom` for the inner enums.
## Example
```rust
use nested_deserialize::NestedDeserialize;
use strum::VariantNames;
#[derive(Deserialize, VariantNames, Debug)] // `VariantNames` required, subject to change
enum Physics { // "Inner" enum
Velocity { m_s: f64 },
Acceleration { m_s2: f64 },
}
#[derive(Deserialize, VariantNames, Debug)]
enum Chemistry { // "Inner" enum
MolarMass { g_mol: f64 },
PhValue { ph: f64 },
}
#[derive(NestedDeserialize, Debug)] // This crate provides `NestedDeserialize`
enum ScienceData { // "Outer" enum
Phys(Physics),
Chem(Chemistry),
}
```
## Differences with other solutions
### [`#[serde(untagged)]`](https://serde.rs/enum-representations.html#untagged)
* `#[serde(untagged)]` tries to parse as each variant in order
* `nested-deserialize` delegates to variants based on the tag (can only support enums)
<br/><br/>
* `#[serde(untagged)]` discards any errors from parsing as the inner variants
* `nested-deserialize` provides specialized errors for the following:
* unknown variant (tag)
* invalid tagged enum shape
* the error from the variant, if a valid variant was specified, but could not be deserialized into
### [`deserialize_untagged_verbose_error`](https://crates.io/crates/deserialize_untagged_verbose_error)
* `deserialize_untagged_verbose_error` tries to parse as each variant in order (like `#[serde(untagged)]`)
* `nested-deserialize` delegates to variants based on the tag (can only support enums)
<br/><br/>
* `deserialize_untagged_verbose_error` returns errors from _all variants it tried_
* which is good for debugging
* ..but most likely you already knew which variant you were trying to use
* `nested-deserialize` provides the error from trying to deserialize as the specified variant
* or provides a list of valid variants if none matched
* or provides a generic error message in case of an invalid type
### [`serde-untagged`](https://crates.io/crates/serde-untagged)
* `serde-untagged` is not a proc macro
* requires writing boilerplate
* ultimate control
* `nested-deserialize` is a proc macro
* apply it to an enum and done
* may not satisfy complicated use-cases
## AI usage disclosure
I vibecoded this, it's about 150 lines of code (pretty simple), and it seems to work for the described usecase. I plan to rewrite parts if needed, by hand, once I fully understand how proc macros work.
If you're interested in this crate/need this functionality, and willing to spend some time helping me with polishing it, please reach out to me.
## To-Do / Wishlist
* remove dependency on `strum`
* support [adjacently tagged enums](https://serde.rs/enum-representations.html#adjacently-tagged) (the more popular type)
* recursive nesting