Skip to content

Commit cf150b5

Browse files
authored
fix(quick-xml): Fixed sequence-based text values (enums) not being supported in attributes. (#114)
* Add inline attribute declaration tests. * Identified issue with enum values of attributes for xmlity-quick-xml. * Fixed. * `cargo clippy`
1 parent 3054e15 commit cf150b5

File tree

3 files changed

+158
-16
lines changed

3 files changed

+158
-16
lines changed

xmlity-quick-xml/src/de.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,13 +355,15 @@ impl<'de> de::AttributeAccess<'de> for AttributeAccess<'_, 'de> {
355355
T::deserialize(TextDeserializer {
356356
value: self.value,
357357
deserializer: self.deserializer,
358+
used_up: false,
358359
})
359360
}
360361
}
361362

362363
struct TextDeserializer<'a, 'v> {
363364
value: Cow<'v, [u8]>,
364365
deserializer: &'a Deserializer<'a>,
366+
used_up: bool,
365367
}
366368

367369
impl<'de> de::XmlText<'de> for TextDeserializer<'_, 'de> {
@@ -394,6 +396,61 @@ impl<'de> de::XmlText<'de> for TextDeserializer<'_, 'de> {
394396
}
395397
}
396398

399+
impl<'de> de::SeqAccess<'de> for TextDeserializer<'_, 'de> {
400+
type Error = Error;
401+
402+
type SubAccess<'g>
403+
= TextDeserializer<'g, 'de>
404+
where
405+
Self: 'g;
406+
407+
fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
408+
where
409+
T: Deserialize<'de>,
410+
{
411+
if self.used_up {
412+
return Ok(None);
413+
}
414+
415+
T::deserialize(TextDeserializer {
416+
value: self.value.clone(),
417+
deserializer: self.deserializer,
418+
used_up: false,
419+
})
420+
.map(|value| {
421+
self.used_up = true;
422+
Some(value)
423+
})
424+
}
425+
426+
fn next_element_seq<T>(&mut self) -> Result<Option<T>, Self::Error>
427+
where
428+
T: Deserialize<'de>,
429+
{
430+
if self.used_up {
431+
return Ok(None);
432+
}
433+
434+
T::deserialize_seq(TextDeserializer {
435+
value: self.value.clone(),
436+
deserializer: self.deserializer,
437+
used_up: false,
438+
})
439+
.map(|value| {
440+
self.used_up = true;
441+
Some(value)
442+
})
443+
}
444+
445+
fn sub_access(&mut self) -> Result<Self::SubAccess<'_>, Self::Error> {
446+
Ok(TextDeserializer {
447+
value: self.value.clone(),
448+
deserializer: self.deserializer,
449+
used_up: self.used_up,
450+
})
451+
}
452+
}
453+
397454
impl<'de> de::Deserializer<'de> for TextDeserializer<'_, 'de> {
398455
type Error = Error;
399456

@@ -408,7 +465,7 @@ impl<'de> de::Deserializer<'de> for TextDeserializer<'_, 'de> {
408465
where
409466
V: Visitor<'de>,
410467
{
411-
visitor.visit_text(self)
468+
visitor.visit_seq(self)
412469
}
413470
}
414471

xmlity-quick-xml/src/ser.rs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,19 +348,49 @@ pub struct AttributeSerializer<'t, W: Write> {
348348

349349
/// The text serializer for the `quick-xml` crate. Used when serializing to an attribute value.
350350
pub struct TextSerializer {
351-
value: Vec<u8>,
351+
value: Option<String>,
352352
}
353353

354-
impl ser::Serializer for &mut TextSerializer {
354+
impl ser::SerializeSeq for &mut TextSerializer {
355+
type Ok = ();
356+
type Error = Error;
357+
358+
fn serialize_element<V: Serialize>(&mut self, value: &V) -> Result<Self::Ok, Self::Error> {
359+
if self.value.is_some() {
360+
return Err(Error::unexpected_serialize(Unexpected::Text));
361+
}
362+
363+
let mut text_ser = TextSerializer { value: None };
364+
value.serialize(&mut text_ser)?;
365+
366+
if let Some(value) = text_ser.value {
367+
self.value = Some(value);
368+
} else {
369+
return Err(Error::unexpected_serialize(Unexpected::None));
370+
}
371+
372+
Ok(())
373+
}
374+
375+
fn end(self) -> Result<Self::Ok, Self::Error> {
376+
Ok(())
377+
}
378+
}
379+
380+
impl<'a> ser::Serializer for &'a mut TextSerializer {
355381
type Ok = ();
356382
type Error = Error;
357383

358384
type SerializeElement = NoopDeSerializer<Self::Ok, Self::Error>;
359385

360-
type SerializeSeq = NoopDeSerializer<Self::Ok, Self::Error>;
386+
type SerializeSeq = &'a mut TextSerializer;
361387

362388
fn serialize_text<S: AsRef<str>>(self, text: S) -> Result<Self::Ok, Self::Error> {
363-
self.value.extend_from_slice(text.as_ref().as_bytes());
389+
if self.value.is_some() {
390+
return Err(Error::unexpected_serialize(Unexpected::Text));
391+
}
392+
393+
self.value = Some(text.as_ref().to_string());
364394

365395
Ok(())
366396
}
@@ -381,7 +411,7 @@ impl ser::Serializer for &mut TextSerializer {
381411
}
382412

383413
fn serialize_seq(self) -> Result<Self::SerializeSeq, Self::Error> {
384-
Err(Error::unexpected_serialize(Unexpected::Seq))
414+
Ok(self)
385415
}
386416

387417
fn serialize_decl<S: AsRef<str>>(
@@ -447,11 +477,17 @@ impl<W: Write> ser::SerializeAttributeAccess for AttributeSerializer<'_, W> {
447477
self.serializer.push_decl_attr(decl);
448478
}
449479

450-
let mut text_ser = TextSerializer { value: Vec::new() };
480+
let mut text_ser = TextSerializer { value: None };
451481

452482
value.serialize(&mut text_ser)?;
453483

454-
self.serializer.push_attr(qname, text_ser.value);
484+
self.serializer.push_attr(
485+
qname,
486+
text_ser
487+
.value
488+
.expect("TextSerializer should have a value")
489+
.into_bytes(),
490+
);
455491

456492
Ok(())
457493
}

xmlity-quick-xml/tests/elements/inline_attribute_declarations.rs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,39 @@ use crate::define_test;
22

33
use xmlity::{Deserialize, Serialize};
44

5+
fn f_serialize<T: xmlity::Serializer>(f: &F, serializer: T) -> Result<T::Ok, T::Error> {
6+
serializer.serialize_text(&f.0)
7+
}
8+
9+
fn f_deserialize<'de, T: xmlity::Deserializer<'de>>(deserializer: T) -> Result<F, T::Error> {
10+
let s = String::deserialize(deserializer)?;
11+
Ok(F(s))
12+
}
13+
14+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
15+
#[xvalue(serialize_with = "f_serialize", deserialize_with = "f_deserialize")]
16+
struct F(pub String);
17+
518
#[derive(Debug, PartialEq, Serialize, Deserialize)]
619
#[xelement(name = "c")]
7-
pub struct C {
20+
struct C {
821
#[xattribute(name = "b")]
9-
pub c: String,
22+
pub c: F,
1023
}
1124

1225
define_test!(
1326
element_with_single_child,
14-
[(C { c: "A".to_string() }, r#"<c b="A"/>"#)]
27+
[(
28+
C {
29+
c: F("A".to_string())
30+
},
31+
r#"<c b="A"/>"#
32+
)]
1533
);
1634

1735
#[derive(Debug, PartialEq, Serialize, Deserialize)]
1836
#[xelement(name = "d")]
19-
pub struct D {
37+
struct D {
2038
#[xattribute(name = "b")]
2139
pub b: String,
2240
pub c: C,
@@ -27,15 +45,17 @@ define_test!(
2745
[(
2846
D {
2947
b: "A".to_string(),
30-
c: C { c: "B".to_string() }
48+
c: C {
49+
c: F("B".to_string())
50+
}
3151
},
3252
r#"<d b="A"><c b="B"/></d>"#
3353
)]
3454
);
3555

3656
#[derive(Debug, PartialEq, Serialize, Deserialize)]
3757
#[xelement(name = "e")]
38-
pub struct E {
58+
struct E {
3959
pub d: Vec<D>,
4060
}
4161

@@ -46,14 +66,43 @@ define_test!(
4666
d: vec![
4767
D {
4868
b: "A".to_string(),
49-
c: C { c: "B".to_string() }
69+
c: C {
70+
c: F("B".to_string())
71+
}
5072
},
5173
D {
5274
b: "C".to_string(),
53-
c: C { c: "D".to_string() }
75+
c: C {
76+
c: F("D".to_string())
77+
}
5478
}
5579
]
5680
},
5781
r#"<e><d b="A"><c b="B"/></d><d b="C"><c b="D"/></d></e>"#
5882
)]
5983
);
84+
85+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
86+
enum H {
87+
F(F),
88+
}
89+
90+
#[derive(Debug, PartialEq, Serialize, Deserialize)]
91+
#[xelement(name = "g")]
92+
struct G {
93+
#[xattribute(name = "f", optional, default)]
94+
pub f: Option<H>,
95+
}
96+
97+
define_test!(
98+
element_with_optional_attribute_of_enum,
99+
[
100+
(
101+
G {
102+
f: Some(H::F(F("A".to_string())))
103+
},
104+
r#"<g f="A"/>"#
105+
),
106+
(G { f: None }, r#"<g/>"#)
107+
]
108+
);

0 commit comments

Comments
 (0)