Skip to content

Comments

Refactoring logic to handle enum newtype variants#949

Open
coderBlitz wants to merge 1 commit intotafia:masterfrom
coderBlitz:dev
Open

Refactoring logic to handle enum newtype variants#949
coderBlitz wants to merge 1 commit intotafia:masterfrom
coderBlitz:dev

Conversation

@coderBlitz
Copy link

I need this crate to support enum newtype variants if possible, and these changes so far seem to do just that. Though I only intended to solve the newtype variant issue, some quick testing seems to show that the deserialize changes actually work for all enum variants now (see below)! This would addresses a significant portion of #717, leaving only the serialization of the tuple and struct variants to be done; also the documentation portion.

I did modify some of the test cases, but everything still passes.

Quick examples:

use quick_xml::{de, se};
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct Bla {
    f1: Enum,
    my_val: String,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
enum Enum {
    Newtype(u64),
    Tuple(u64, String),
    Struct { a: u64, b: String },
}

fn main() {
    const NEWTYPE: &str = "<Bla><f1><Newtype>5</Newtype><my_val>Hello world</my_val></f1></Bla>";
    let bla = Bla {
        f1: Enum::Newtype(5),
        my_val: "Hello world".to_string(),
    };
    assert_eq!(bla, de::from_str(NEWTYPE).unwrap());

    const TUPLE: &str =
        "<Bla><f1><Tuple>5</Tuple><Tuple>Hi</Tuple><my_val>Hello world</my_val></f1></Bla>";
    let bla = Bla {
        f1: Enum::Tuple(5, "Hi".to_string()),
        my_val: "Hello world".to_string(),
    };
    assert_eq!(bla, de::from_str(TUPLE).unwrap());

    const STRUCT: &str =
        "<Bla><f1><Struct><a>5</a><b>Hey</b></Struct><my_val>Hello world</my_val></f1></Bla>";
    let bla = Bla {
        f1: Enum::Struct {
            a: 5,
            b: "Hey".to_string(),
        },
        my_val: "Hello world".to_string(),
    };
    assert_eq!(bla, de::from_str(STRUCT).unwrap());
}

@Mingun Mingun added the serde Issues related to mapping from Rust types to XML label Feb 24, 2026
@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.42%. Comparing base (2b21d40) to head (aae51c4).
⚠️ Report is 26 commits behind head on master.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #949      +/-   ##
==========================================
+ Coverage   55.00%   56.42%   +1.42%     
==========================================
  Files          44       44              
  Lines       16816    17599     +783     
==========================================
+ Hits         9249     9931     +682     
- Misses       7567     7668     +101     
Flag Coverage Δ
unittests 56.42% <100.00%> (+1.42%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@Mingun Mingun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before submitting PR I want to hear how the following types should be mapped to the XML, and why (if their mapping will differ from current), for all possible enum variants (unit, newtype, tuple, struct):

  • Enum
  • Root { field: Enum }
  • Root { #[serde(flatten)] field: Enum }
  • Root { #[serde(rename = "$text")] field: Enum }
  • Root { #[serde(rename = "$value")] field: Enum }

Mapping must be consistent and not create WTF situations.
Mapping must be composable, that means if you know how the type will be serialized, you must predict how it will be serialized (or cannot be serialized) when you put it in:

  • a map key/value
  • a value of a struct field
  • a value of a newtype
  • a value of a tuple element
  • a Some value of an Option

Serialized XML must be deserializable to the original type. If exceptions exists, they should be explained.

Comment on lines +326 to +328
// #XXX - Removed since it seemingly only fails when
// deserializing tuple variant.
//debug_assert_eq!(self.start.name(), e.name());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it failed here, your code contains logical error.

seed.deserialize(BorrowedStrDeserializer::<DeError>::new(TEXT_KEY))?,
true,
),
// SAFETY: we use that deserializer only when we peeked `Start` or `Text` event
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SAFETY markers shouldn't be removed. They should be updated with the explanation, why that code is unreachable.

}

match self.map.de.next()? {
// Handles <field>UnitEnumVariant</field>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That commend should be updated to show what XML shape processed here. This is required because the code became much more complicated

Comment on lines +658 to +663
let mut has_text_key = false;
for variant in variants {
if *variant == "$text" {
has_text_key = true;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code conveys intent more clearly:

Suggested change
let mut has_text_key = false;
for variant in variants {
if *variant == "$text" {
has_text_key = true;
}
}
let has_text_key = variants.contains(TEXT_KEY);

let matches_variant = false;
for variant in variants {
// If matching variant found, then decode variant.
if t.buf == variant.as_bytes() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is wrong check. buf is in t.decoder() encoding, while variant is in UTF-8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

serde Issues related to mapping from Rust types to XML

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants