1extern crate alloc;
2
3use alloc::{borrow::Cow, format, string::String, vec::Vec};
4use std::collections::HashMap;
5use std::io::Write;
6
7use facet_core::{Def, Facet, ScalarType};
8use facet_dom::{DomSerializeError, DomSerializer};
9use facet_reflect::Peek;
10
11use crate::escaping::EscapingWriter;
12
13pub use facet_dom::FloatFormatter;
14
15fn write_scalar_value(
19 out: &mut dyn Write,
20 value: Peek<'_, '_>,
21 float_formatter: Option<FloatFormatter>,
22) -> std::io::Result<bool> {
23 let value = value.innermost_peek();
25
26 if let Def::Option(_) = &value.shape().def
28 && let Ok(opt) = value.into_option()
29 {
30 return match opt.value() {
31 Some(inner) => write_scalar_value(out, inner, float_formatter),
32 None => Ok(false),
33 };
34 }
35
36 let Some(scalar_type) = value.scalar_type() else {
37 if matches!(value.shape().def, Def::Scalar) && value.shape().vtable.has_display() {
39 write!(out, "{}", value)?;
40 return Ok(true);
41 }
42
43 if let Ok(enum_) = value.into_enum()
45 && let Ok(variant) = enum_.active_variant()
46 && variant.data.kind == facet_core::StructKind::Unit
47 {
48 let variant_name = if variant.rename.is_some() {
50 Cow::Borrowed(variant.effective_name())
51 } else {
52 facet_dom::naming::to_element_name(variant.name)
53 };
54 out.write_all(variant_name.as_bytes())?;
55 return Ok(true);
56 }
57
58 return Ok(false);
59 };
60
61 match scalar_type {
62 ScalarType::Unit => {
63 out.write_all(b"null")?;
64 }
65 ScalarType::Bool => {
66 let b = value.get::<bool>().unwrap();
67 out.write_all(if *b { b"true" } else { b"false" })?;
68 }
69 ScalarType::Char => {
70 let c = value.get::<char>().unwrap();
71 let mut buf = [0u8; 4];
72 let s = c.encode_utf8(&mut buf);
73 out.write_all(s.as_bytes())?;
74 }
75 ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
76 let s = value.as_str().unwrap();
77 out.write_all(s.as_bytes())?;
78 }
79 ScalarType::F32 => {
80 let v = value.get::<f32>().unwrap();
81 if let Some(fmt) = float_formatter {
82 fmt(*v as f64, out)?;
83 } else {
84 write!(out, "{}", v)?;
85 }
86 }
87 ScalarType::F64 => {
88 let v = value.get::<f64>().unwrap();
89 if let Some(fmt) = float_formatter {
90 fmt(*v, out)?;
91 } else {
92 write!(out, "{}", v)?;
93 }
94 }
95 ScalarType::U8 => write!(out, "{}", value.get::<u8>().unwrap())?,
96 ScalarType::U16 => write!(out, "{}", value.get::<u16>().unwrap())?,
97 ScalarType::U32 => write!(out, "{}", value.get::<u32>().unwrap())?,
98 ScalarType::U64 => write!(out, "{}", value.get::<u64>().unwrap())?,
99 ScalarType::U128 => write!(out, "{}", value.get::<u128>().unwrap())?,
100 ScalarType::USize => write!(out, "{}", value.get::<usize>().unwrap())?,
101 ScalarType::I8 => write!(out, "{}", value.get::<i8>().unwrap())?,
102 ScalarType::I16 => write!(out, "{}", value.get::<i16>().unwrap())?,
103 ScalarType::I32 => write!(out, "{}", value.get::<i32>().unwrap())?,
104 ScalarType::I64 => write!(out, "{}", value.get::<i64>().unwrap())?,
105 ScalarType::I128 => write!(out, "{}", value.get::<i128>().unwrap())?,
106 ScalarType::ISize => write!(out, "{}", value.get::<isize>().unwrap())?,
107 #[cfg(feature = "net")]
108 ScalarType::IpAddr => write!(out, "{}", value.get::<core::net::IpAddr>().unwrap())?,
109 #[cfg(feature = "net")]
110 ScalarType::Ipv4Addr => write!(out, "{}", value.get::<core::net::Ipv4Addr>().unwrap())?,
111 #[cfg(feature = "net")]
112 ScalarType::Ipv6Addr => write!(out, "{}", value.get::<core::net::Ipv6Addr>().unwrap())?,
113 #[cfg(feature = "net")]
114 ScalarType::SocketAddr => write!(out, "{}", value.get::<core::net::SocketAddr>().unwrap())?,
115 _ => return Ok(false),
116 }
117 Ok(true)
118}
119
120#[derive(Clone)]
122pub struct SerializeOptions {
123 pub pretty: bool,
125 pub indent: Cow<'static, str>,
127 pub float_formatter: Option<FloatFormatter>,
130 pub preserve_entities: bool,
138}
139
140impl Default for SerializeOptions {
141 fn default() -> Self {
142 Self {
143 pretty: false,
144 indent: Cow::Borrowed(" "),
145 float_formatter: None,
146 preserve_entities: false,
147 }
148 }
149}
150
151impl core::fmt::Debug for SerializeOptions {
152 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 f.debug_struct("SerializeOptions")
154 .field("pretty", &self.pretty)
155 .field("indent", &self.indent)
156 .field("float_formatter", &self.float_formatter.map(|_| "..."))
157 .field("preserve_entities", &self.preserve_entities)
158 .finish()
159 }
160}
161
162impl SerializeOptions {
163 pub fn new() -> Self {
165 Self::default()
166 }
167
168 pub const fn pretty(mut self) -> Self {
170 self.pretty = true;
171 self
172 }
173
174 pub fn indent(mut self, indent: impl Into<Cow<'static, str>>) -> Self {
176 self.indent = indent.into();
177 self.pretty = true;
178 self
179 }
180
181 pub fn float_formatter(mut self, formatter: FloatFormatter) -> Self {
215 self.float_formatter = Some(formatter);
216 self
217 }
218
219 pub const fn preserve_entities(mut self, preserve: bool) -> Self {
227 self.preserve_entities = preserve;
228 self
229 }
230}
231
232#[allow(dead_code)] const WELL_KNOWN_NAMESPACES: &[(&str, &str)] = &[
235 ("http://www.w3.org/2001/XMLSchema-instance", "xsi"),
236 ("http://www.w3.org/2001/XMLSchema", "xs"),
237 ("http://www.w3.org/XML/1998/namespace", "xml"),
238 ("http://www.w3.org/1999/xlink", "xlink"),
239 ("http://www.w3.org/2000/svg", "svg"),
240 ("http://www.w3.org/1999/xhtml", "xhtml"),
241 ("http://schemas.xmlsoap.org/soap/envelope/", "soap"),
242 ("http://www.w3.org/2003/05/soap-envelope", "soap12"),
243 ("http://schemas.android.com/apk/res/android", "android"),
244];
245
246#[derive(Debug)]
247pub struct XmlSerializeError {
248 msg: Cow<'static, str>,
249}
250
251impl core::fmt::Display for XmlSerializeError {
252 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
253 f.write_str(&self.msg)
254 }
255}
256
257impl std::error::Error for XmlSerializeError {}
258
259pub struct XmlSerializer {
266 out: Vec<u8>,
267 element_stack: Vec<String>,
269 declared_namespaces: HashMap<String, String>,
271 next_ns_index: usize,
273 current_default_ns: Option<String>,
276 current_ns_all: Option<String>,
278 pending_is_attribute: bool,
280 pending_is_text: bool,
282 pending_is_elements: bool,
284 pending_is_doctype: bool,
286 pending_is_tag: bool,
288 pending_namespace: Option<String>,
290 options: SerializeOptions,
292 depth: usize,
294 collecting_attributes: bool,
296 pending_establish_default_ns: bool,
298}
299
300impl XmlSerializer {
301 pub fn new() -> Self {
303 Self::with_options(SerializeOptions::default())
304 }
305
306 pub fn with_options(options: SerializeOptions) -> Self {
308 Self {
309 out: Vec::new(),
310 element_stack: Vec::new(),
311 declared_namespaces: HashMap::new(),
312 next_ns_index: 0,
313 current_default_ns: None,
314 current_ns_all: None,
315 pending_is_attribute: false,
316 pending_is_text: false,
317 pending_is_elements: false,
318 pending_is_doctype: false,
319 pending_is_tag: false,
320 pending_namespace: None,
321 options,
322 depth: 0,
323 collecting_attributes: false,
324 pending_establish_default_ns: false,
325 }
326 }
327
328 pub fn finish(self) -> Vec<u8> {
329 self.out
330 }
331
332 fn write_element_tag_start(&mut self, name: &str, namespace: Option<&str>) {
335 self.write_indent();
336 self.out.push(b'<');
337
338 let close_tag: String;
340
341 if let Some(ns_uri) = namespace {
343 if self.current_default_ns.as_deref() == Some(ns_uri) {
344 self.out.extend_from_slice(name.as_bytes());
346 close_tag = name.to_string();
347 } else if self.pending_establish_default_ns {
348 self.out.extend_from_slice(name.as_bytes());
350 self.out.extend_from_slice(b" xmlns=\"");
351 self.out.extend_from_slice(ns_uri.as_bytes());
352 self.out.push(b'"');
353 self.current_default_ns = Some(ns_uri.to_string());
354 self.pending_establish_default_ns = false;
355 close_tag = name.to_string();
356 } else {
357 let prefix = self.get_or_create_prefix(ns_uri);
359 self.out.extend_from_slice(prefix.as_bytes());
360 self.out.push(b':');
361 self.out.extend_from_slice(name.as_bytes());
362 self.out.extend_from_slice(b" xmlns:");
364 self.out.extend_from_slice(prefix.as_bytes());
365 self.out.extend_from_slice(b"=\"");
366 self.out.extend_from_slice(ns_uri.as_bytes());
367 self.out.push(b'"');
368 close_tag = format!("{}:{}", prefix, name);
369 }
370 } else {
371 self.out.extend_from_slice(name.as_bytes());
372 close_tag = name.to_string();
373 }
374
375 self.element_stack.push(close_tag);
377 }
378
379 fn write_attribute(
382 &mut self,
383 name: &str,
384 value: Peek<'_, '_>,
385 namespace: Option<&str>,
386 ) -> std::io::Result<bool> {
387 let mut value_buf = Vec::new();
389 let written = write_scalar_value(
390 &mut EscapingWriter::attribute(&mut value_buf),
391 value,
392 self.options.float_formatter,
393 )?;
394
395 if !written {
396 return Ok(false);
398 }
399
400 self.out.push(b' ');
402 if let Some(ns_uri) = namespace {
403 let prefix = self.get_or_create_prefix(ns_uri);
404 self.out.extend_from_slice(b"xmlns:");
406 self.out.extend_from_slice(prefix.as_bytes());
407 self.out.extend_from_slice(b"=\"");
408 self.out.extend_from_slice(ns_uri.as_bytes());
409 self.out.extend_from_slice(b"\" ");
410 self.out.extend_from_slice(prefix.as_bytes());
412 self.out.push(b':');
413 }
414 self.out.extend_from_slice(name.as_bytes());
415 self.out.extend_from_slice(b"=\"");
416 self.out.extend_from_slice(&value_buf);
417 self.out.push(b'"');
418 Ok(true)
419 }
420
421 fn write_element_tag_end(&mut self) {
423 self.out.push(b'>');
424 self.write_newline();
425 self.depth += 1;
426 }
427
428 fn write_close_tag(&mut self, name: &str) {
429 self.depth = self.depth.saturating_sub(1);
430 self.write_indent();
431 self.out.extend_from_slice(b"</");
432 self.out.extend_from_slice(name.as_bytes());
433 self.out.push(b'>');
434 self.write_newline();
435 }
436
437 fn write_text_escaped(&mut self, text: &str) {
438 use std::io::Write;
439 if self.options.preserve_entities {
440 let escaped = escape_preserving_entities(text, false);
441 self.out.extend_from_slice(escaped.as_bytes());
442 } else {
443 let _ = EscapingWriter::text(&mut self.out).write_all(text.as_bytes());
445 }
446 }
447
448 fn write_indent(&mut self) {
450 if self.options.pretty {
451 for _ in 0..self.depth {
452 self.out.extend_from_slice(self.options.indent.as_bytes());
453 }
454 }
455 }
456
457 fn write_newline(&mut self) {
459 if self.options.pretty {
460 self.out.push(b'\n');
461 }
462 }
463
464 fn get_or_create_prefix(&mut self, namespace_uri: &str) -> String {
466 if let Some(prefix) = self.declared_namespaces.get(namespace_uri) {
468 return prefix.clone();
469 }
470
471 let prefix = WELL_KNOWN_NAMESPACES
473 .iter()
474 .find(|(uri, _)| *uri == namespace_uri)
475 .map(|(_, prefix)| (*prefix).to_string())
476 .unwrap_or_else(|| {
477 let prefix = format!("ns{}", self.next_ns_index);
479 self.next_ns_index += 1;
480 prefix
481 });
482
483 let final_prefix = if self.declared_namespaces.values().any(|p| p == &prefix) {
485 let prefix = format!("ns{}", self.next_ns_index);
486 self.next_ns_index += 1;
487 prefix
488 } else {
489 prefix
490 };
491
492 self.declared_namespaces
493 .insert(namespace_uri.to_string(), final_prefix.clone());
494 final_prefix
495 }
496
497 fn clear_field_state_impl(&mut self) {
498 self.pending_is_attribute = false;
499 self.pending_is_text = false;
500 self.pending_is_elements = false;
501 self.pending_is_doctype = false;
502 self.pending_is_tag = false;
503 self.pending_namespace = None;
504 }
505}
506
507impl Default for XmlSerializer {
508 fn default() -> Self {
509 Self::new()
510 }
511}
512
513impl DomSerializer for XmlSerializer {
514 type Error = XmlSerializeError;
515
516 fn element_start(&mut self, tag: &str, namespace: Option<&str>) -> Result<(), Self::Error> {
517 let ns = namespace
519 .map(|s| s.to_string())
520 .or_else(|| self.pending_namespace.take())
521 .or_else(|| self.current_ns_all.clone());
522
523 self.write_element_tag_start(tag, ns.as_deref());
525 self.collecting_attributes = true;
526
527 Ok(())
528 }
529
530 fn attribute(
531 &mut self,
532 name: &str,
533 value: Peek<'_, '_>,
534 namespace: Option<&str>,
535 ) -> Result<(), Self::Error> {
536 if !self.collecting_attributes {
538 return Err(XmlSerializeError {
539 msg: Cow::Borrowed("attribute() called after children_start()"),
540 });
541 }
542
543 let ns: Option<String> = match namespace {
545 Some(ns) => Some(ns.to_string()),
546 None => self.pending_namespace.clone(),
547 };
548
549 self.write_attribute(name, value, ns.as_deref())
551 .map_err(|e| XmlSerializeError {
552 msg: Cow::Owned(format!("write error: {}", e)),
553 })?;
554 Ok(())
555 }
556
557 fn children_start(&mut self) -> Result<(), Self::Error> {
558 self.write_element_tag_end();
560 self.collecting_attributes = false;
561 Ok(())
562 }
563
564 fn children_end(&mut self) -> Result<(), Self::Error> {
565 Ok(())
566 }
567
568 fn element_end(&mut self, _tag: &str) -> Result<(), Self::Error> {
569 if let Some(close_tag) = self.element_stack.pop() {
570 self.write_close_tag(&close_tag);
571 }
572 Ok(())
573 }
574
575 fn text(&mut self, content: &str) -> Result<(), Self::Error> {
576 self.write_text_escaped(content);
577 Ok(())
578 }
579
580 fn struct_metadata(&mut self, shape: &facet_core::Shape) -> Result<(), Self::Error> {
581 self.current_ns_all = shape
583 .attributes
584 .iter()
585 .find(|attr| attr.ns == Some("xml") && attr.key == "ns_all")
586 .and_then(|attr| attr.get_as::<&str>().copied())
587 .map(String::from);
588
589 self.pending_establish_default_ns = self.current_ns_all.is_some();
591
592 Ok(())
593 }
594
595 fn field_metadata(&mut self, field: &facet_reflect::FieldItem) -> Result<(), Self::Error> {
596 let Some(field_def) = field.field else {
597 self.pending_is_attribute = true;
599 self.pending_is_text = false;
600 self.pending_is_elements = false;
601 self.pending_is_doctype = false;
602 self.pending_is_tag = false;
603 return Ok(());
604 };
605
606 self.pending_is_attribute = field_def.get_attr(Some("xml"), "attribute").is_some();
608 self.pending_is_text = field_def.get_attr(Some("xml"), "text").is_some();
610 self.pending_is_elements = field_def.get_attr(Some("xml"), "elements").is_some();
612 self.pending_is_doctype = field_def.get_attr(Some("xml"), "doctype").is_some();
614 self.pending_is_tag = field_def.get_attr(Some("xml"), "tag").is_some();
616
617 if let Some(ns_attr) = field_def.get_attr(Some("xml"), "ns")
619 && let Some(ns_uri) = ns_attr.get_as::<&str>().copied()
620 {
621 self.pending_namespace = Some(ns_uri.to_string());
622 } else if !self.pending_is_attribute && !self.pending_is_text {
623 self.pending_namespace = self.current_ns_all.clone();
625 } else {
626 self.pending_namespace = None;
628 }
629
630 Ok(())
631 }
632
633 fn variant_metadata(
634 &mut self,
635 _variant: &'static facet_core::Variant,
636 ) -> Result<(), Self::Error> {
637 Ok(())
638 }
639
640 fn is_attribute_field(&self) -> bool {
641 self.pending_is_attribute
642 }
643
644 fn is_text_field(&self) -> bool {
645 self.pending_is_text
646 }
647
648 fn is_elements_field(&self) -> bool {
649 self.pending_is_elements
650 }
651
652 fn is_doctype_field(&self) -> bool {
653 self.pending_is_doctype
654 }
655
656 fn is_tag_field(&self) -> bool {
657 self.pending_is_tag
658 }
659
660 fn doctype(&mut self, content: &str) -> Result<(), Self::Error> {
661 self.out.write_all(b"<!DOCTYPE ").unwrap();
663 self.out.write_all(content.as_bytes()).unwrap();
664 self.out.write_all(b">").unwrap();
665 if self.options.pretty {
666 self.out.write_all(b"\n").unwrap();
667 }
668 Ok(())
669 }
670
671 fn clear_field_state(&mut self) {
672 self.clear_field_state_impl();
673 }
674
675 fn format_float(&self, value: f64) -> String {
676 if let Some(formatter) = self.options.float_formatter {
677 let mut buf = Vec::new();
678 if formatter(value, &mut buf).is_ok()
680 && let Ok(s) = String::from_utf8(buf)
681 {
682 return s;
683 }
684 }
685 value.to_string()
686 }
687
688 fn serialize_none(&mut self) -> Result<(), Self::Error> {
689 Ok(())
691 }
692
693 fn format_namespace(&self) -> Option<&'static str> {
694 Some("xml")
695 }
696}
697
698pub fn to_vec<'facet, T>(value: &'_ T) -> Result<Vec<u8>, DomSerializeError<XmlSerializeError>>
700where
701 T: Facet<'facet> + ?Sized,
702{
703 to_vec_with_options(value, &SerializeOptions::default())
704}
705
706pub fn to_vec_with_options<'facet, T>(
708 value: &'_ T,
709 options: &SerializeOptions,
710) -> Result<Vec<u8>, DomSerializeError<XmlSerializeError>>
711where
712 T: Facet<'facet> + ?Sized,
713{
714 let mut serializer = XmlSerializer::with_options(options.clone());
715 facet_dom::serialize(&mut serializer, Peek::new(value))?;
716 Ok(serializer.finish())
717}
718
719pub fn to_string<'facet, T>(value: &'_ T) -> Result<String, DomSerializeError<XmlSerializeError>>
721where
722 T: Facet<'facet> + ?Sized,
723{
724 let bytes = to_vec(value)?;
725 Ok(String::from_utf8(bytes).expect("XmlSerializer produces valid UTF-8"))
727}
728
729pub fn to_string_pretty<'facet, T>(
731 value: &'_ T,
732) -> Result<String, DomSerializeError<XmlSerializeError>>
733where
734 T: Facet<'facet> + ?Sized,
735{
736 to_string_with_options(value, &SerializeOptions::default().pretty())
737}
738
739pub fn to_string_with_options<'facet, T>(
741 value: &'_ T,
742 options: &SerializeOptions,
743) -> Result<String, DomSerializeError<XmlSerializeError>>
744where
745 T: Facet<'facet> + ?Sized,
746{
747 let bytes = to_vec_with_options(value, options)?;
748 Ok(String::from_utf8(bytes).expect("XmlSerializer produces valid UTF-8"))
750}
751
752fn escape_preserving_entities(s: &str, is_attribute: bool) -> String {
759 let mut result = String::with_capacity(s.len());
760 let chars: Vec<char> = s.chars().collect();
761 let mut i = 0;
762
763 while i < chars.len() {
764 let c = chars[i];
765 match c {
766 '<' => result.push_str("<"),
767 '>' => result.push_str(">"),
768 '"' if is_attribute => result.push_str("""),
769 '&' => {
770 if let Some(entity_len) = try_parse_entity_reference(&chars[i..]) {
772 for j in 0..entity_len {
774 result.push(chars[i + j]);
775 }
776 i += entity_len;
777 continue;
778 } else {
779 result.push_str("&");
781 }
782 }
783 _ => result.push(c),
784 }
785 i += 1;
786 }
787
788 result
789}
790
791fn try_parse_entity_reference(chars: &[char]) -> Option<usize> {
799 if chars.is_empty() || chars[0] != '&' {
800 return None;
801 }
802
803 if chars.len() < 3 {
805 return None;
806 }
807
808 let mut len = 1; if chars[1] == '#' {
811 len = 2;
813
814 if len < chars.len() && (chars[len] == 'x' || chars[len] == 'X') {
815 len += 1;
817 let start = len;
818 while len < chars.len() && chars[len].is_ascii_hexdigit() {
819 len += 1;
820 }
821 if len == start {
823 return None;
824 }
825 } else {
826 let start = len;
828 while len < chars.len() && chars[len].is_ascii_digit() {
829 len += 1;
830 }
831 if len == start {
833 return None;
834 }
835 }
836 } else {
837 if !chars[len].is_ascii_alphabetic() && chars[len] != '_' {
839 return None;
840 }
841 len += 1;
842 while len < chars.len()
843 && (chars[len].is_ascii_alphanumeric()
844 || chars[len] == '_'
845 || chars[len] == '-'
846 || chars[len] == '.')
847 {
848 len += 1;
849 }
850 }
851
852 if len >= chars.len() || chars[len] != ';' {
854 return None;
855 }
856
857 Some(len + 1) }