1use alloc::borrow::Cow;
10
11use rxml::{parser::EventMetrics, AttrMap, Event, Namespace, NcName, NcNameStr, XmlVersion};
12
13#[derive(Debug)]
23pub enum Item<'x> {
24 XmlDeclaration(XmlVersion),
26
27 ElementHeadStart(
29 Namespace<'x>,
31 Cow<'x, NcNameStr>,
33 ),
34
35 Attribute(
37 Namespace<'x>,
39 Cow<'x, NcNameStr>,
41 Cow<'x, str>,
43 ),
44
45 ElementHeadEnd,
47
48 Text(Cow<'x, str>),
50
51 ElementFoot,
61}
62
63impl Item<'_> {
64 pub fn into_owned(self) -> Item<'static> {
67 match self {
68 Self::XmlDeclaration(v) => Item::XmlDeclaration(v),
69 Self::ElementHeadStart(ns, name) => {
70 Item::ElementHeadStart(ns.into_static(), Cow::Owned(name.into_owned()))
71 }
72 Self::Attribute(ns, name, value) => Item::Attribute(
73 ns.into_static(),
74 Cow::Owned(name.into_owned()),
75 Cow::Owned(value.into_owned()),
76 ),
77 Self::ElementHeadEnd => Item::ElementHeadEnd,
78 Self::Text(value) => Item::Text(Cow::Owned(value.into_owned())),
79 Self::ElementFoot => Item::ElementFoot,
80 }
81 }
82
83 pub fn as_rxml_item(&self) -> rxml::Item<'_> {
85 match self {
86 Self::XmlDeclaration(ref v) => rxml::Item::XmlDeclaration(*v),
87 Self::ElementHeadStart(ref ns, ref name) => {
88 rxml::Item::ElementHeadStart(ns.clone(), name)
89 }
90 Self::Attribute(ref ns, ref name, ref value) => {
91 rxml::Item::Attribute(ns.clone(), name, value)
92 }
93 Self::ElementHeadEnd => rxml::Item::ElementHeadEnd,
94 Self::Text(ref value) => rxml::Item::Text(value),
95 Self::ElementFoot => rxml::Item::ElementFoot,
96 }
97 }
98}
99
100#[cfg(feature = "minidom")]
106pub(crate) struct EventToItem<I> {
107 inner: I,
108 attributes: Option<rxml::xml_map::IntoIter<alloc::string::String>>,
109}
110
111#[cfg(feature = "minidom")]
112impl<I> EventToItem<I> {
113 pub(crate) fn new(inner: I) -> Self {
114 Self {
115 inner,
116 attributes: None,
117 }
118 }
119
120 fn drain(&mut self) -> Option<Item<'static>> {
121 match self.attributes {
122 Some(ref mut attrs) => {
123 if let Some(((ns, name), value)) = attrs.next() {
124 Some(Item::Attribute(ns, Cow::Owned(name), Cow::Owned(value)))
125 } else {
126 self.attributes = None;
127 Some(Item::ElementHeadEnd)
128 }
129 }
130 None => None,
131 }
132 }
133
134 fn update(&mut self, ev: Event) -> Item<'static> {
135 assert!(self.attributes.is_none());
136 match ev {
137 Event::XmlDeclaration(_, v) => Item::XmlDeclaration(v),
138 Event::StartElement(_, (ns, name), attrs) => {
139 self.attributes = Some(attrs.into_iter());
140 Item::ElementHeadStart(ns, Cow::Owned(name))
141 }
142 Event::Text(_, value) => Item::Text(Cow::Owned(value)),
143 Event::EndElement(_) => Item::ElementFoot,
144 }
145 }
146}
147
148#[cfg(feature = "minidom")]
149impl<I: Iterator<Item = Result<Event, crate::error::Error>>> Iterator for EventToItem<I> {
150 type Item = Result<Item<'static>, crate::error::Error>;
151
152 fn next(&mut self) -> Option<Self::Item> {
153 if let Some(item) = self.drain() {
154 return Some(Ok(item));
155 }
156 let next = match self.inner.next() {
157 Some(Ok(v)) => v,
158 Some(Err(e)) => return Some(Err(e)),
159 None => return None,
160 };
161 Some(Ok(self.update(next)))
162 }
163
164 fn size_hint(&self) -> (usize, Option<usize>) {
165 (self.inner.size_hint().0, None)
168 }
169}
170
171pub(crate) struct ItemToEvent<I> {
177 inner: I,
178 event_buffer: Option<Event>,
179 elem_buffer: Option<(Namespace<'static>, NcName, AttrMap)>,
180}
181
182impl<'x, I: Iterator<Item = Result<Item<'x>, crate::error::Error>>> ItemToEvent<I> {
183 pub(crate) fn new(inner: I) -> Self {
185 Self {
186 inner,
187 event_buffer: None,
188 elem_buffer: None,
189 }
190 }
191}
192
193impl<I> ItemToEvent<I> {
194 fn update(&mut self, item: Item<'_>) -> Result<Option<Event>, crate::error::Error> {
195 assert!(self.event_buffer.is_none());
196 match item {
197 Item::XmlDeclaration(v) => {
198 assert!(self.elem_buffer.is_none());
199 Ok(Some(Event::XmlDeclaration(EventMetrics::zero(), v)))
200 }
201 Item::ElementHeadStart(ns, name) => {
202 if self.elem_buffer.is_some() {
203 panic!("got a second ElementHeadStart items without ElementHeadEnd inbetween: ns={:?} name={:?} (state={:?})", ns, name, self.elem_buffer);
207 }
208 self.elem_buffer = Some((ns.into_static(), name.into_owned(), AttrMap::new()));
209 Ok(None)
210 }
211 Item::Attribute(ns, name, value) => {
212 let Some((_, _, attrs)) = self.elem_buffer.as_mut() else {
213 panic!(
217 "got a second Attribute item without ElementHeadStart: ns={:?}, name={:?}",
218 ns, name
219 );
220 };
221 attrs.insert(ns.into_static(), name.into_owned(), value.into_owned());
222 Ok(None)
223 }
224 Item::ElementHeadEnd => {
225 let Some((ns, name, attrs)) = self.elem_buffer.take() else {
226 panic!(
230 "got ElementHeadEnd item without ElementHeadStart: {:?}",
231 item
232 );
233 };
234 Ok(Some(Event::StartElement(
235 EventMetrics::zero(),
236 (ns, name),
237 attrs,
238 )))
239 }
240 Item::Text(value) => {
241 if let Some(elem_buffer) = self.elem_buffer.as_ref() {
242 panic!("got Text after ElementHeadStart but before ElementHeadEnd: Text({:?}) (state = {:?})", value, elem_buffer);
246 }
247 Ok(Some(Event::Text(EventMetrics::zero(), value.into_owned())))
248 }
249 Item::ElementFoot => {
250 let end_ev = Event::EndElement(EventMetrics::zero());
251 let result = if let Some((ns, name, attrs)) = self.elem_buffer.take() {
252 self.event_buffer = Some(end_ev);
254 Event::StartElement(EventMetrics::zero(), (ns, name), attrs)
255 } else {
256 end_ev
257 };
258 Ok(Some(result))
259 }
260 }
261 }
262}
263
264impl<'x, I: Iterator<Item = Result<Item<'x>, crate::error::Error>>> Iterator for ItemToEvent<I> {
265 type Item = Result<Event, crate::error::Error>;
266
267 fn next(&mut self) -> Option<Self::Item> {
268 if let Some(event) = self.event_buffer.take() {
269 return Some(Ok(event));
270 }
271 loop {
272 let item = match self.inner.next() {
273 Some(Ok(v)) => v,
274 Some(Err(e)) => return Some(Err(e)),
275 None => return None,
276 };
277 if let Some(v) = self.update(item).transpose() {
278 return Some(v);
279 }
280 }
281 }
282
283 fn size_hint(&self) -> (usize, Option<usize>) {
284 (self.inner.size_hint().0, None)
287 }
288}
289
290#[cfg(all(test, feature = "minidom"))]
291mod tests_minidom {
292 use super::*;
293
294 use alloc::{borrow::ToOwned, string::ToString, vec, vec::Vec};
295
296 fn events_to_items<I: Iterator<Item = Event>>(events: I) -> Vec<Item<'static>> {
297 let iter = EventToItem {
298 inner: events.map(|ev| Ok(ev)),
299 attributes: None,
300 };
301 let mut result = Vec::new();
302 for item in iter {
303 let item = item.unwrap();
304 result.push(item);
305 }
306 result
307 }
308
309 #[test]
310 fn event_to_item_xml_declaration() {
311 let events = vec![Event::XmlDeclaration(
312 EventMetrics::zero(),
313 XmlVersion::V1_0,
314 )];
315 let items = events_to_items(events.into_iter());
316 assert_eq!(items.len(), 1);
317 match items[0] {
318 Item::XmlDeclaration(XmlVersion::V1_0) => (),
319 ref other => panic!("unexpected item in position 0: {:?}", other),
320 };
321 }
322
323 #[test]
324 fn event_to_item_empty_element() {
325 let events = vec![
326 Event::StartElement(
327 EventMetrics::zero(),
328 (Namespace::NONE, "elem".try_into().unwrap()),
329 AttrMap::new(),
330 ),
331 Event::EndElement(EventMetrics::zero()),
332 ];
333 let items = events_to_items(events.into_iter());
334 assert_eq!(items.len(), 3);
335 match items[0] {
336 Item::ElementHeadStart(ref ns, ref name) => {
337 assert_eq!(&**ns, Namespace::none());
338 assert_eq!(&**name, "elem");
339 }
340 ref other => panic!("unexpected item in position 0: {:?}", other),
341 };
342 match items[1] {
343 Item::ElementHeadEnd => (),
344 ref other => panic!("unexpected item in position 1: {:?}", other),
345 };
346 match items[2] {
347 Item::ElementFoot => (),
348 ref other => panic!("unexpected item in position 2: {:?}", other),
349 };
350 }
351
352 #[test]
353 fn event_to_item_element_with_attributes() {
354 let mut attrs = AttrMap::new();
355 attrs.insert(
356 Namespace::NONE,
357 "attr".try_into().unwrap(),
358 "value".to_string(),
359 );
360 let events = vec![
361 Event::StartElement(
362 EventMetrics::zero(),
363 (Namespace::NONE, "elem".try_into().unwrap()),
364 attrs,
365 ),
366 Event::EndElement(EventMetrics::zero()),
367 ];
368 let items = events_to_items(events.into_iter());
369 assert_eq!(items.len(), 4);
370 match items[0] {
371 Item::ElementHeadStart(ref ns, ref name) => {
372 assert_eq!(&**ns, Namespace::none());
373 assert_eq!(&**name, "elem");
374 }
375 ref other => panic!("unexpected item in position 0: {:?}", other),
376 };
377 match items[1] {
378 Item::Attribute(ref ns, ref name, ref value) => {
379 assert_eq!(&**ns, Namespace::none());
380 assert_eq!(&**name, "attr");
381 assert_eq!(&**value, "value");
382 }
383 ref other => panic!("unexpected item in position 1: {:?}", other),
384 };
385 match items[2] {
386 Item::ElementHeadEnd => (),
387 ref other => panic!("unexpected item in position 2: {:?}", other),
388 };
389 match items[3] {
390 Item::ElementFoot => (),
391 ref other => panic!("unexpected item in position 3: {:?}", other),
392 };
393 }
394
395 #[test]
396 fn event_to_item_element_with_text() {
397 let events = vec![
398 Event::StartElement(
399 EventMetrics::zero(),
400 (Namespace::NONE, "elem".try_into().unwrap()),
401 AttrMap::new(),
402 ),
403 Event::Text(EventMetrics::zero(), "Hello World!".to_owned()),
404 Event::EndElement(EventMetrics::zero()),
405 ];
406 let items = events_to_items(events.into_iter());
407 assert_eq!(items.len(), 4);
408 match items[0] {
409 Item::ElementHeadStart(ref ns, ref name) => {
410 assert_eq!(&**ns, Namespace::none());
411 assert_eq!(&**name, "elem");
412 }
413 ref other => panic!("unexpected item in position 0: {:?}", other),
414 };
415 match items[1] {
416 Item::ElementHeadEnd => (),
417 ref other => panic!("unexpected item in position 1: {:?}", other),
418 };
419 match items[2] {
420 Item::Text(ref value) => {
421 assert_eq!(value, "Hello World!");
422 }
423 ref other => panic!("unexpected item in position 2: {:?}", other),
424 };
425 match items[3] {
426 Item::ElementFoot => (),
427 ref other => panic!("unexpected item in position 3: {:?}", other),
428 };
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 use alloc::{vec, vec::Vec};
437
438 fn items_to_events<'x, I: IntoIterator<Item = Item<'x>>>(
439 items: I,
440 ) -> Result<Vec<Event>, crate::error::Error> {
441 let iter = ItemToEvent {
442 inner: items.into_iter().map(|x| Ok(x)),
443 event_buffer: None,
444 elem_buffer: None,
445 };
446 let mut result = Vec::new();
447 for ev in iter {
448 let ev = ev?;
449 result.push(ev);
450 }
451 Ok(result)
452 }
453
454 #[test]
455 fn item_to_event_xml_decl() {
456 let items = vec![Item::XmlDeclaration(XmlVersion::V1_0)];
457 let events = items_to_events(items).expect("item conversion");
458 assert_eq!(events.len(), 1);
459 match events[0] {
460 Event::XmlDeclaration(_, XmlVersion::V1_0) => (),
461 ref other => panic!("unexpected event in position 0: {:?}", other),
462 };
463 }
464
465 #[test]
466 fn item_to_event_simple_empty_element() {
467 let items = vec![
468 Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())),
469 Item::ElementHeadEnd,
470 Item::ElementFoot,
471 ];
472 let events = items_to_events(items).expect("item conversion");
473 assert_eq!(events.len(), 2);
474 match events[0] {
475 Event::StartElement(_, (ref ns, ref name), ref attrs) => {
476 assert_eq!(attrs.len(), 0);
477 assert_eq!(ns, Namespace::none());
478 assert_eq!(name, "elem");
479 }
480 ref other => panic!("unexpected event in position 0: {:?}", other),
481 };
482 match events[1] {
483 Event::EndElement(_) => (),
484 ref other => panic!("unexpected event in position 1: {:?}", other),
485 };
486 }
487
488 #[test]
489 fn item_to_event_short_empty_element() {
490 let items = vec![
491 Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())),
492 Item::ElementFoot,
493 ];
494 let events = items_to_events(items).expect("item conversion");
495 assert_eq!(events.len(), 2);
496 match events[0] {
497 Event::StartElement(_, (ref ns, ref name), ref attrs) => {
498 assert_eq!(attrs.len(), 0);
499 assert_eq!(ns, Namespace::none());
500 assert_eq!(name, "elem");
501 }
502 ref other => panic!("unexpected event in position 0: {:?}", other),
503 };
504 match events[1] {
505 Event::EndElement(_) => (),
506 ref other => panic!("unexpected event in position 1: {:?}", other),
507 };
508 }
509
510 #[test]
511 fn item_to_event_element_with_text_content() {
512 let items = vec![
513 Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())),
514 Item::ElementHeadEnd,
515 Item::Text(Cow::Borrowed("Hello World!")),
516 Item::ElementFoot,
517 ];
518 let events = items_to_events(items).expect("item conversion");
519 assert_eq!(events.len(), 3);
520 match events[0] {
521 Event::StartElement(_, (ref ns, ref name), ref attrs) => {
522 assert_eq!(attrs.len(), 0);
523 assert_eq!(ns, Namespace::none());
524 assert_eq!(name, "elem");
525 }
526 ref other => panic!("unexpected event in position 0: {:?}", other),
527 };
528 match events[1] {
529 Event::Text(_, ref value) => {
530 assert_eq!(value, "Hello World!");
531 }
532 ref other => panic!("unexpected event in position 1: {:?}", other),
533 };
534 match events[2] {
535 Event::EndElement(_) => (),
536 ref other => panic!("unexpected event in position 2: {:?}", other),
537 };
538 }
539
540 #[test]
541 fn item_to_event_element_with_attributes() {
542 let items = vec![
543 Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())),
544 Item::Attribute(
545 Namespace::NONE,
546 Cow::Borrowed("attr".try_into().unwrap()),
547 Cow::Borrowed("value"),
548 ),
549 Item::ElementHeadEnd,
550 Item::ElementFoot,
551 ];
552 let events = items_to_events(items).expect("item conversion");
553 assert_eq!(events.len(), 2);
554 match events[0] {
555 Event::StartElement(_, (ref ns, ref name), ref attrs) => {
556 assert_eq!(ns, Namespace::none());
557 assert_eq!(name, "elem");
558 assert_eq!(attrs.len(), 1);
559 assert_eq!(
560 attrs.get(Namespace::none(), "attr").map(|x| x.as_str()),
561 Some("value")
562 );
563 }
564 ref other => panic!("unexpected event in position 0: {:?}", other),
565 };
566 match events[1] {
567 Event::EndElement(_) => (),
568 ref other => panic!("unexpected event in position 2: {:?}", other),
569 };
570 }
571}