Skip to content

Commit bbe4652

Browse files
authored
fix(value): Fix stack overflow for subvalue in struct from value. (#125)
1 parent 2ec4873 commit bbe4652

File tree

7 files changed

+201
-19
lines changed

7 files changed

+201
-19
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod indirect_direct_equal;
2+
pub mod sub_xml_value;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use xmlity::{value::XmlText, Deserialize, Serialize, XmlValue};
2+
3+
use crate::define_deserialize_test;
4+
5+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
6+
#[xelement(name = "a", namespace = "http://example.com/test")]
7+
pub struct A {
8+
#[xvalue(default)]
9+
pub values: Vec<XmlValue>,
10+
}
11+
define_deserialize_test!(
12+
a_deserialize,
13+
[(
14+
A {
15+
values: vec![xmlity::xml!(
16+
<"b":"http://example.com/test">["\n\t\t"
17+
<"c":"http://example.com/test">["Fixed Assets"]</"c">"\n\t\t"
18+
<"c":"http://example.com/test">["Change in Retained Earnings"]</"c">"\n\t"
19+
]</"b">"\n"
20+
)
21+
.into()],
22+
},
23+
XML.trim()
24+
)]
25+
);
26+
27+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
28+
#[xelement(
29+
name = "b",
30+
namespace = "http://example.com/test",
31+
allow_unknown_attributes = "any"
32+
)]
33+
pub struct B {
34+
#[xvalue(default)]
35+
pub c: Vec<C>,
36+
}
37+
38+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
39+
#[xelement(
40+
name = "c",
41+
namespace = "http://example.com/test",
42+
allow_unknown_attributes = "any"
43+
)]
44+
#[xgroup(children_order = "strict")]
45+
pub struct C {
46+
pub value: XmlValue,
47+
}
48+
49+
const XML: &str = r###"
50+
<a xmlns="http://example.com/test">
51+
<b>
52+
<c>Fixed Assets</c>
53+
<c>Change in Retained Earnings</c>
54+
</b>
55+
</a>
56+
"###;
57+
58+
#[test]
59+
fn indirect() {
60+
let a =
61+
xmlity_quick_xml::from_str::<A>(XML.trim()).expect("Failed to parse schema from string");
62+
63+
let b_values = a
64+
.values
65+
.iter()
66+
.map(|a| B::deserialize(a).unwrap())
67+
.collect::<Vec<_>>();
68+
69+
assert_eq!(
70+
b_values,
71+
vec![B {
72+
c: vec![
73+
C {
74+
value: XmlText::new("Fixed Assets").into()
75+
},
76+
C {
77+
value: XmlText::new("Change in Retained Earnings").into()
78+
}
79+
],
80+
},]
81+
);
82+
}

xmlity/src/value/deserialize.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,36 @@ use core::marker::PhantomData;
88
use super::*;
99

1010
impl<'de> Deserialize<'de> for XmlValue {
11+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
12+
where
13+
D: Deserializer<'de>,
14+
{
15+
XmlValueWithoutSeq::deserialize(deserializer).map(From::from)
16+
}
17+
18+
fn deserialize_seq<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
19+
deserializer
20+
.deserialize_seq(IteratorVisitor::<_, XmlSeq<XmlValueWithoutSeq>>::default())
21+
.map(|mut a| match a.values.len() {
22+
0 => XmlValue::None,
23+
1 => a.values.pop_front().expect("Just checked.").into(),
24+
_ => XmlValue::Seq(a.values.into_iter().map(From::from).collect()),
25+
})
26+
}
27+
}
28+
29+
impl<'de> Deserialize<'de> for XmlValueWithoutSeq {
1130
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1231
where
1332
D: Deserializer<'de>,
1433
{
1534
struct __Visitor<'v> {
16-
marker: PhantomData<XmlValue>,
35+
marker: PhantomData<XmlValueWithoutSeq>,
1736
lifetime: PhantomData<&'v ()>,
1837
}
1938

2039
impl<'v> crate::de::Visitor<'v> for __Visitor<'v> {
21-
type Value = XmlValue;
40+
type Value = XmlValueWithoutSeq;
2241

2342
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2443
formatter.write_str("a comment")
@@ -29,7 +48,9 @@ impl<'de> Deserialize<'de> for XmlValue {
2948
E: de::Error,
3049
V: de::XmlText<'v>,
3150
{
32-
XmlTextVisitor::new().visit_text(value).map(XmlValue::Text)
51+
XmlTextVisitor::new()
52+
.visit_text(value)
53+
.map(XmlValueWithoutSeq::Text)
3354
}
3455

3556
fn visit_cdata<E, V>(self, value: V) -> Result<Self::Value, E>
@@ -39,7 +60,7 @@ impl<'de> Deserialize<'de> for XmlValue {
3960
{
4061
XmlCDataVisitor::new()
4162
.visit_cdata(value)
42-
.map(XmlValue::CData)
63+
.map(XmlValueWithoutSeq::CData)
4364
}
4465

4566
fn visit_element<A>(self, element: A) -> Result<Self::Value, A::Error>
@@ -48,16 +69,7 @@ impl<'de> Deserialize<'de> for XmlValue {
4869
{
4970
XmlElementVisitor::new()
5071
.visit_element(element)
51-
.map(XmlValue::Element)
52-
}
53-
54-
fn visit_seq<S>(self, sequence: S) -> Result<Self::Value, S::Error>
55-
where
56-
S: de::SeqAccess<'v>,
57-
{
58-
IteratorVisitor::<_, XmlSeq<XmlValue>>::default()
59-
.visit_seq(sequence)
60-
.map(XmlValue::Seq)
72+
.map(XmlValueWithoutSeq::Element)
6173
}
6274

6375
fn visit_pi<E, V>(self, value: V) -> Result<Self::Value, E>
@@ -67,7 +79,7 @@ impl<'de> Deserialize<'de> for XmlValue {
6779
{
6880
XmlProcessingInstructionVisitor::new()
6981
.visit_pi(value)
70-
.map(XmlValue::PI)
82+
.map(XmlValueWithoutSeq::PI)
7183
}
7284

7385
fn visit_decl<E, V>(self, declaration: V) -> Result<Self::Value, E>
@@ -77,7 +89,7 @@ impl<'de> Deserialize<'de> for XmlValue {
7789
{
7890
XmlDeclVisitor::new()
7991
.visit_decl(declaration)
80-
.map(XmlValue::Decl)
92+
.map(XmlValueWithoutSeq::Decl)
8193
}
8294

8395
fn visit_comment<E, V>(self, comment: V) -> Result<Self::Value, E>
@@ -87,7 +99,7 @@ impl<'de> Deserialize<'de> for XmlValue {
8799
{
88100
XmlCommentVisitor::new()
89101
.visit_comment(comment)
90-
.map(XmlValue::Comment)
102+
.map(XmlValueWithoutSeq::Comment)
91103
}
92104

93105
fn visit_doctype<E, V>(self, value: V) -> Result<Self::Value, E>
@@ -97,14 +109,14 @@ impl<'de> Deserialize<'de> for XmlValue {
97109
{
98110
XmlDoctypeVisitor::new()
99111
.visit_doctype(value)
100-
.map(XmlValue::Doctype)
112+
.map(XmlValueWithoutSeq::Doctype)
101113
}
102114

103115
fn visit_none<E>(self) -> Result<Self::Value, E>
104116
where
105117
E: de::Error,
106118
{
107-
Ok(XmlValue::None)
119+
Ok(XmlValueWithoutSeq::None)
108120
}
109121
}
110122

xmlity/src/value/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,44 @@ impl From<XmlDoctype> for XmlValue {
106106
}
107107
}
108108

109+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
110+
enum XmlValueWithoutSeq {
111+
/// A text node.
112+
Text(XmlText),
113+
/// A CDATA section.
114+
CData(XmlCData),
115+
/// An element.
116+
Element(XmlElement),
117+
/// A processing instruction.
118+
PI(XmlProcessingInstruction),
119+
/// A declaration.
120+
Decl(XmlDecl),
121+
/// A comment.
122+
Comment(XmlComment),
123+
/// A doctype.
124+
Doctype(XmlDoctype),
125+
/// Nothing.
126+
#[default]
127+
None,
128+
}
129+
130+
impl From<XmlValueWithoutSeq> for XmlValue {
131+
fn from(value: XmlValueWithoutSeq) -> Self {
132+
match value {
133+
XmlValueWithoutSeq::Text(xml_text) => XmlValue::Text(xml_text),
134+
XmlValueWithoutSeq::CData(xml_cdata) => XmlValue::CData(xml_cdata),
135+
XmlValueWithoutSeq::Element(xml_element) => XmlValue::Element(xml_element),
136+
XmlValueWithoutSeq::PI(xml_processing_instruction) => {
137+
XmlValue::PI(xml_processing_instruction)
138+
}
139+
XmlValueWithoutSeq::Decl(xml_decl) => XmlValue::Decl(xml_decl),
140+
XmlValueWithoutSeq::Comment(xml_comment) => XmlValue::Comment(xml_comment),
141+
XmlValueWithoutSeq::Doctype(xml_doctype) => XmlValue::Doctype(xml_doctype),
142+
XmlValueWithoutSeq::None => XmlValue::None,
143+
}
144+
}
145+
}
146+
109147
/// A text node in an XML document.
110148
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
111149
#[non_exhaustive]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use xmlity::{Deserialize, Serialize, XmlValue};
2+
3+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
4+
#[xelement(
5+
name = "b",
6+
namespace = "http://example.com/test",
7+
allow_unknown_attributes = "any"
8+
)]
9+
pub struct B {
10+
#[xvalue(default)]
11+
pub c: Vec<C>,
12+
}
13+
14+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
15+
#[xelement(
16+
name = "c",
17+
namespace = "http://example.com/test",
18+
allow_unknown_attributes = "any"
19+
)]
20+
#[xgroup(children_order = "strict")]
21+
pub struct C {
22+
pub value: XmlValue,
23+
}
24+
25+
#[test]
26+
fn value() {
27+
let value = xmlity::xml!(<"b":"http://example.com/test">["\n\t\t"
28+
<"c":"http://example.com/test">["Fixed Assets"]</"c">"\n\t\t"
29+
<"c":"http://example.com/test">["Change in Retained Earnings"]</"c">"\n\t"
30+
]</"b">);
31+
32+
let b = B::deserialize(&value).unwrap();
33+
34+
assert_eq!(
35+
b,
36+
B {
37+
c: vec![
38+
C {
39+
value: XmlValue::Text("Fixed Assets".into())
40+
},
41+
C {
42+
value: XmlValue::Text("Change in Retained Earnings".into())
43+
},
44+
],
45+
}
46+
)
47+
}

xmlity/tests/elements/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod element_with_value;

xmlity/tests/features.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod elements;

0 commit comments

Comments
 (0)