serde_roxmltree/
raw_node.rs1use std::cell::Cell;
2use std::fmt;
3use std::marker::PhantomData;
4use std::mem::transmute;
5use std::ops::Deref;
6use std::ptr;
7
8use roxmltree::Node;
9use serde_core::de;
10
11use crate::{Deserializer, Source};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub struct RawNode<'a>(pub Node<'a, 'a>);
38
39impl<'a> Deref for RawNode<'a> {
40 type Target = Node<'a, 'a>;
41
42 fn deref(&self) -> &Self::Target {
43 &self.0
44 }
45}
46
47impl<'de, 'a> de::Deserialize<'de> for RawNode<'a>
48where
49 'de: 'a,
50{
51 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
52 where
53 D: de::Deserializer<'de>,
54 {
55 struct Visitor<'a>(PhantomData<&'a ()>);
56
57 impl<'de, 'a> de::Visitor<'de> for Visitor<'a>
58 where
59 'de: 'a,
60 {
61 type Value = RawNode<'a>;
62
63 fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
64 fmt.write_str("struct RawNode")
65 }
66
67 fn visit_map<M>(self, _map: M) -> Result<Self::Value, M::Error>
68 where
69 M: de::MapAccess<'de>,
70 {
71 match CURR_NODE.get() {
72 #[allow(unsafe_code)]
73 Some(curr_node) => Ok(RawNode(unsafe {
75 transmute::<Node<'static, 'static>, Node<'a, 'a>>(curr_node)
76 })),
77 None => Err(de::Error::custom("no current node")),
78 }
79 }
80 }
81
82 deserializer.deserialize_struct(RAW_NODE_NAME, &[], Visitor(PhantomData))
83 }
84}
85
86pub fn deserialize_struct<'de, 'input, 'temp, O, F, R>(
87 this: Deserializer<'de, 'input, 'temp, O>,
88 name: &'static str,
89 f: F,
90) -> R
91where
92 F: FnOnce(Deserializer<'de, 'input, 'temp, O>) -> R,
93{
94 let _reset_curr_node = match &this.source {
95 Source::Node(node) if ptr::eq(name, RAW_NODE_NAME) => {
96 let reset_curr_node = ResetCurrNode(CURR_NODE.get());
97
98 #[allow(unsafe_code)]
99 CURR_NODE.set(Some(unsafe {
101 transmute::<Node<'de, 'input>, Node<'static, 'static>>(*node)
102 }));
103
104 Some(reset_curr_node)
105 }
106 _ => None,
107 };
108
109 f(this)
110}
111
112static RAW_NODE_NAME: &str = "RawNode";
113
114thread_local! {
115 static CURR_NODE: Cell<Option<Node<'static, 'static>>> = const { Cell::new(None) };
116}
117
118struct ResetCurrNode(Option<Node<'static, 'static>>);
119
120impl Drop for ResetCurrNode {
121 fn drop(&mut self) {
122 CURR_NODE.set(self.0.take());
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 use roxmltree::Document;
131 use serde::Deserialize;
132
133 use crate::from_doc;
134
135 #[test]
136 fn raw_node_captures_subtree() {
137 #[derive(Debug, Deserialize)]
138 struct Root<'a> {
139 #[serde(borrow)]
140 foo: RawNode<'a>,
141 }
142
143 let doc = Document::parse(r#"<root><foo><bar qux="42">23</bar>baz</foo></root>"#).unwrap();
144 let val = from_doc::<Root>(&doc).unwrap();
145
146 assert!(val.foo.0.is_element());
147 assert!(val.foo.0.has_tag_name("foo"));
148
149 let children = val.foo.0.children().collect::<Vec<_>>();
150 assert_eq!(children.len(), 2);
151 assert!(children[0].is_element());
152 assert!(children[0].has_tag_name("bar"));
153 assert_eq!(children[0].attribute("qux").unwrap(), "42");
154 assert!(children[1].is_text());
155 assert_eq!(children[1].text().unwrap(), "baz");
156 }
157}