nested-deserialize 0.1.0

A very special-case implementation of serde::Deserialize for untagged nested enums.
Documentation
  • Coverage
  • 0%
    0 out of 2 items documented0 out of 1 items with examples
  • Size
  • Source code size: 14.22 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 283.11 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 5s Average build duration of successful builds.
  • all releases: 5s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • verarr

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
    • and strum::VariantNames
      • 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

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)]

  • #[serde(untagged)] tries to parse as each variant in order
  • nested-deserialize delegates to variants based on the tag (can only support enums)
  • #[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

  • 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)
  • 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

  • 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