#enums #string #serde #serialization #proc-macro

macro no-std serde_string_enum

Procedural macros for serde serialization and deserialization of string-encoded enums

3 unstable releases

0.2.1 Aug 8, 2023
0.2.0 Aug 8, 2023
0.1.0 Aug 6, 2023

#610 in #enums

Download history 20/week @ 2024-04-03 4/week @ 2024-05-15 20/week @ 2024-05-22 9/week @ 2024-05-29 24/week @ 2024-06-05 19/week @ 2024-06-12 2/week @ 2024-06-19 7/week @ 2024-07-10 62/week @ 2024-07-17

69 downloads per month
Used in battler

MIT license

20KB
326 lines

serde_string_enum

Latest Version

This crate provides a procedural macro to automatically derive serde's Serialize and Deserialize traits for enum types that should be encoded as a single string.

[dependencies]
serde = "1.0"
serde_string_enum = "0.2"
unicase = "2.6.0"

Usage

This crate defines two pairs of macros:

  • SerializeLabeledStringEnum / DeserializeLabeledStringEnum - Uses the #[string = ...] attribute on each enum variant to perform string conversions.
  • SerializeStringEnum / DeserializeStringEnum - Uses the enum type's Display and FromStr implementations to perform string conversions.

Features

  • default - std, unicase
  • std - Depend on the Rust standard library.
  • alloc - Depend on the alloc library without the Rust standard library.
  • unicase - Depend on the unicase crate for Unicode-insensitive matching.

Examples:

Labeled Strings

#[cfg(feature = "alloc")]
extern crate alloc;

use serde_string_enum::{
    DeserializeLabeledStringEnum,
    SerializeLabeledStringEnum,
};

#[derive(Debug, PartialEq, SerializeLabeledStringEnum, DeserializeLabeledStringEnum)]
enum Type {
    #[string = "Grass"]
    Grass,
    #[string = "Fire"]
    #[alias = "Flame"]
    Fire,
    #[string = "Water"]
    Water,
}

fn main() -> serde_json::Result<()> {
    let j = serde_json::to_string(&Type::Grass)?;
    assert_eq!(j, "\"Grass\"");
    let t: Type = serde_json::from_str(&j)?;
    assert_eq!(t, Type::Grass);

    // Alias strings.
    let t: Type = serde_json::from_str("\"Flame\"")?;
    assert_eq!(t, Type::Fire);

    // Case-insensitive conversion also works.
    if cfg!(feature = "unicase") {
        let t: Type = serde_json::from_str("\"water\"")?;
        assert_eq!(t, Type::Water);
    }

    Ok(())
}

Enums with Display and FromStr

use core::{
    fmt::Display,
    str::FromStr,
};
use serde_string_enum::{
    DeserializeStringEnum,
    SerializeStringEnum,
};

#[derive(Debug, PartialEq, SerializeStringEnum, DeserializeStringEnum)]
enum Move {
    Stay,
    Forward(u8),
    Left(u8),
}

impl Display for Move {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Stay => write!(f, "S"),
            Self::Forward(n) => write!(f, "F{n}"),
            Self::Left(n) => write!(f, "L{n}"),
        }
    }
}

impl FromStr for Move {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(match &s[0..1] {
            "S" => Self::Stay,
            "F" => Self::Forward(s[1..].parse::<u8>().map_err(|err| err.to_string())?),
            "L" => Self::Left(s[1..].parse::<u8>().map_err(|err| err.to_string())?),
            _ => return Err(format!("invalid move {s}")),
        })
    }
}

fn main() -> serde_json::Result<()> {
    let j = serde_json::to_string(&Move::Forward(10))?;
    assert_eq!(j, "\"F10\"");
    let m: Move = serde_json::from_str(&j)?;
    assert_eq!(m, Move::Forward(10));

    let moves: Vec<Move> = serde_json::from_str("[\"S\",\"F2\",\"S\",\"L4\"]")?;
    assert_eq!(
        moves,
        vec![Move::Stay, Move::Forward(2), Move::Stay, Move::Left(4)]
    );

    Ok(())
}

Dependencies

~0.5–1MB
~25K SLoC