1use std::borrow::Cow;
2use std::fmt::Write as _;
3
4use deser_core::ext::Number;
5use deser_core::hints::Layout;
6use deser_core::ser::SerializeRef;
7use deser_core::ser::{self, Describe, EventSink, SerializeDriver};
8use deser_core::{Atom, BytesFormat, Error, ErrorKind, Event, Serialize, State};
9
10use crate::Names;
11use crate::de::XML_NAMESPACE;
12use crate::mixed::KeepsWhitespace;
13use crate::root::{Declarations, RootData};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
19#[non_exhaustive]
20pub enum Indent {
21 #[default]
23 None,
24 Spaces(usize),
27 Tab,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct SerializerConfig {
81 names: Names,
82 root: Option<&'static str>,
83 declaration: bool,
84 indent: Indent,
85 context: deser_core::Context,
86}
87
88impl Default for SerializerConfig {
89 fn default() -> SerializerConfig {
90 SerializerConfig::new()
91 }
92}
93
94impl SerializerConfig {
95 pub const fn new() -> SerializerConfig {
97 SerializerConfig {
98 names: Names::new(),
99 root: None,
100 declaration: false,
101 indent: Indent::None,
102 context: deser_core::Context::new(),
103 }
104 }
105
106 pub const fn builder() -> SerializerConfigBuilder {
108 SerializerConfigBuilder::new()
109 }
110
111 pub const fn into_builder(self) -> SerializerConfigBuilder {
113 SerializerConfigBuilder { value: self }
114 }
115
116 pub fn set_context(&mut self, context: deser_core::Context) {
124 self.context = context;
125 }
126
127 pub fn context(&self) -> &deser_core::Context {
129 &self.context
130 }
131
132 #[inline]
134 fn apply_context(&self, driver: &mut SerializeDriver<'_>) {
135 if !self.context.is_empty() {
136 driver.set_default_context(self.context.clone());
137 }
138 }
139
140 pub const fn set_root(&mut self, name: &'static str) {
148 self.root = Some(name);
149 }
150
151 pub const fn set_attribute_prefix(&mut self, prefix: &'static str) {
153 self.names.attribute_prefix = prefix;
154 }
155
156 pub const fn set_text_key(&mut self, key: &'static str) {
158 self.names.text_key = key;
159 }
160
161 pub const fn namespaces(
209 mut self,
210 namespaces: &'static [(&'static str, &'static str)],
211 ) -> SerializerConfig {
212 self.names.namespaces = namespaces;
213 self
214 }
215
216 pub const fn set_declaration(&mut self, yes: bool) {
218 self.declaration = yes;
219 }
220
221 pub const fn set_indent(&mut self, indent: Indent) {
267 self.indent = indent;
268 }
269
270 pub const fn set_pretty(&mut self, indent: Indent) {
288 self.set_indent(indent);
289 }
290
291 pub fn to_string<T: Serialize + ?Sized>(&self, value: &T) -> Result<String, Error> {
293 self.to_string_ref(SerializeRef::new(&value))
294 }
295
296 pub fn to_string_with<F, T: Serialize + ?Sized>(
301 &self,
302 value: &T,
303 setup: F,
304 ) -> Result<String, Error>
305 where
306 F: FnOnce(&mut SerializeDriver<'_>),
307 {
308 let mut driver = SerializeDriver::new(&value);
309 setup(&mut driver);
310 self.apply_context(&mut driver);
311 self.serialize_driver(&mut driver)
312 }
313
314 fn to_string_ref(&self, value: SerializeRef<'_>) -> Result<String, Error> {
320 let mut driver = SerializeDriver::from_ref(value);
321 self.apply_context(&mut driver);
322 self.serialize_driver(&mut driver)
323 }
324
325 fn serialize_driver(&self, driver: &mut SerializeDriver<'_>) -> Result<String, Error> {
327 let mut writer = Writer::new(self, BytesFormat::of(driver.state()));
328 driver.drive_sink(&mut writer)?;
329 writer.finish()?;
330 Ok(writer.out)
331 }
332
333 pub(crate) fn serialize_part(
343 &self,
344 value: &mut Option<Box<Writer>>,
345 driver: &mut SerializeDriver<'_>,
346 out: &mut String,
347 limit: usize,
348 ) -> Result<bool, Error> {
349 if value.is_none() && limit == usize::MAX {
351 let document = self.serialize_driver(driver)?;
352 if out.is_empty() {
353 *out = document;
354 } else {
355 out.push_str(&document);
356 }
357 return Ok(true);
358 }
359 let mut writer = value
360 .take()
361 .unwrap_or_else(|| Box::new(Writer::new(self, BytesFormat::of(driver.state()))));
362 let done = if limit == usize::MAX {
364 driver.drive_sink(&mut *writer)?;
365 true
366 } else {
367 writer.limit = limit;
368 driver.drive_until(&mut *writer)?
369 };
370 if let Some(err) = writer.error.take() {
371 return Err(err);
372 }
373 if done {
374 writer.finish()?;
375 out.push_str(&writer.out);
376 return Ok(true);
377 }
378 writer.pass_on(out);
379 *value = Some(writer);
380 Ok(false)
381 }
382}
383
384#[derive(Debug, Clone)]
388#[must_use]
389pub struct SerializerConfigBuilder {
390 value: SerializerConfig,
391}
392
393impl SerializerConfigBuilder {
394 pub const fn new() -> SerializerConfigBuilder {
396 SerializerConfigBuilder {
397 value: SerializerConfig::new(),
398 }
399 }
400
401 pub const fn root(mut self, name: &'static str) -> SerializerConfigBuilder {
405 self.value.set_root(name);
406 self
407 }
408
409 pub const fn attribute_prefix(mut self, prefix: &'static str) -> SerializerConfigBuilder {
413 self.value.set_attribute_prefix(prefix);
414 self
415 }
416
417 pub const fn text_key(mut self, key: &'static str) -> SerializerConfigBuilder {
421 self.value.set_text_key(key);
422 self
423 }
424
425 pub const fn declaration(mut self, yes: bool) -> SerializerConfigBuilder {
429 self.value.set_declaration(yes);
430 self
431 }
432
433 pub const fn indent(mut self, indent: Indent) -> SerializerConfigBuilder {
437 self.value.set_indent(indent);
438 self
439 }
440
441 pub const fn pretty(mut self, indent: Indent) -> SerializerConfigBuilder {
445 self.value.set_pretty(indent);
446 self
447 }
448
449 pub fn context(mut self, context: deser_core::Context) -> SerializerConfigBuilder {
453 self.value.set_context(context);
454 self
455 }
456
457 pub const fn build(self) -> SerializerConfig {
459 let value = unsafe { core::ptr::read(&self.value) };
463 core::mem::forget(self);
464 value
465 }
466}
467
468impl Default for SerializerConfigBuilder {
469 fn default() -> SerializerConfigBuilder {
470 SerializerConfigBuilder::new()
471 }
472}
473
474pub struct Serializer {
509 config: SerializerConfig,
510 out: String,
511 written: bool,
512 document: Option<Box<Writer>>,
514 in_progress: bool,
516}
517
518impl Default for Serializer {
519 fn default() -> Serializer {
520 Serializer::new()
521 }
522}
523
524impl Clone for Serializer {
525 fn clone(&self) -> Serializer {
531 Serializer {
532 config: self.config.clone(),
533 out: self.out.clone(),
534 written: self.written,
535 document: None,
536 in_progress: self.in_progress,
537 }
538 }
539}
540
541impl std::fmt::Debug for Serializer {
542 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543 f.debug_struct("Serializer")
544 .field("config", &self.config)
545 .field("output", &self.out)
546 .field("written", &self.written)
547 .field("in_progress", &self.in_progress)
548 .finish()
549 }
550}
551
552impl Serializer {
553 pub fn new() -> Serializer {
555 Serializer::with_config(SerializerConfig::new())
556 }
557
558 pub fn with_config(config: SerializerConfig) -> Serializer {
560 Serializer {
561 config,
562 out: String::new(),
563 written: false,
564 document: None,
565 in_progress: false,
566 }
567 }
568
569 pub fn config(&self) -> &SerializerConfig {
571 &self.config
572 }
573
574 pub fn written(&self) -> bool {
576 self.written
577 }
578
579 pub fn serialize<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
583 ser::Serializer::serialize(self, value)
584 }
585
586 pub fn serialize_with<F, T: Serialize + ?Sized>(
591 &mut self,
592 value: &T,
593 setup: F,
594 ) -> Result<(), Error>
595 where
596 F: FnOnce(&mut SerializeDriver<'_>),
597 {
598 ser::Serializer::serialize_with(self, value, setup)
599 }
600
601 pub fn as_str(&self) -> &str {
603 &self.out
604 }
605
606 pub fn finish(self) -> String {
608 self.out
609 }
610}
611
612impl ser::Serializer for Serializer {
613 fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
614 if self.in_progress {
616 return Err(Error::in_progress());
617 }
618 ser::StreamSerializer::drive_partial(self, driver, usize::MAX).map(|_| ())
619 }
620}
621
622impl ser::StreamSerializer for Serializer {
623 fn output(&self) -> &[u8] {
624 self.out.as_bytes()
625 }
626
627 fn clear_output(&mut self) {
628 self.out.clear();
629 }
630
631 fn supports_partial(&self) -> bool {
632 true
633 }
634
635 fn drive_partial(
636 &mut self,
637 driver: &mut SerializeDriver<'_>,
638 limit: usize,
639 ) -> Result<bool, Error> {
640 if !self.config.context.is_empty() {
641 driver.set_default_context(self.config.context.clone());
642 }
643 if self.document.is_none() {
644 if self.in_progress {
645 return Err(Error::in_progress());
646 }
647 if self.written {
648 return Err(Error::new(
649 ErrorKind::InvalidState,
650 "an XML document holds a single root element",
651 ));
652 }
653 }
654 if !self
657 .config
658 .serialize_part(&mut self.document, driver, &mut self.out, limit)?
659 {
660 self.in_progress = true;
661 return Ok(false);
662 }
663 self.in_progress = false;
664 self.written = true;
665 Ok(true)
666 }
667
668 fn in_progress(&self) -> bool {
669 self.in_progress
670 }
671}
672
673#[cfg(feature = "io")]
674impl SerializerConfig {
675 pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
700 deser_core::io::Writer::new(writer, Serializer::with_config(self.clone()))
701 }
702
703 pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
707 &self,
708 writer: W,
709 value: &T,
710 ) -> Result<(), Error> {
711 self.writer(writer).write(value)
712 }
713}
714
715#[cfg(feature = "io")]
733pub fn to_writer<W: std::io::Write, T: Serialize + ?Sized>(
734 writer: W,
735 value: &T,
736) -> Result<(), Error> {
737 SerializerConfig::new().to_writer(writer, value)
738}
739
740pub fn to_string<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
744 SerializerConfig::new().to_string(value)
745}
746
747enum Frame {
749 Element(Element),
750 Items {
752 name: String,
753 compact: bool,
755 started: bool,
757 },
758}
759
760#[derive(Clone, Copy, PartialEq, Eq)]
762enum Lines {
763 Pending,
765 Indented,
767 Inline,
769}
770
771struct Element {
773 name: String,
775 attrs_at: usize,
777 content: bool,
779 late: String,
782 attrs: Attrs,
784 fields: Option<(&'static [&'static str], usize)>,
787 bindings: usize,
789 lines: Lines,
791 expanded: bool,
793}
794
795enum Attrs {
800 Unknown,
802 Until(usize),
805 Done,
807}
808
809enum Key {
811 Attribute {
813 name: String,
814 last: bool,
815 },
816 Text,
817 Element(String),
818}
819
820pub(crate) struct Writer {
822 config: SerializerConfig,
823 bytes: BytesFormat,
825 out: String,
828 base: usize,
829 limit: usize,
832 error: Option<Error>,
834 stack: Vec<Frame>,
835 key: Option<Key>,
837 root_bindings: Vec<(String, String)>,
840 root_declared: bool,
843 root_declarations: Option<usize>,
845 local_bindings: Vec<(String, String)>,
847 depth: usize,
849}
850
851#[derive(Default)]
853struct TypeName(Option<String>);
854
855impl Describe for TypeName {
856 fn structure(&mut self, name: &str) {
857 self.0.get_or_insert_with(|| name.to_string());
858 }
859
860 fn newtype(&mut self, name: &str) {
861 self.0.get_or_insert_with(|| name.to_string());
862 }
863
864 fn tuple_struct(&mut self, name: &str) {
865 self.0.get_or_insert_with(|| name.to_string());
866 }
867
868 fn unit_struct(&mut self, name: &str) {
869 self.0.get_or_insert_with(|| name.to_string());
870 }
871}
872
873#[derive(Default)]
879struct Fields {
880 names: Option<&'static [&'static str]>,
881 variant: bool,
882}
883
884impl Describe for Fields {
885 fn structure(&mut self, _name: &str) {
886 self.names = None;
887 }
888
889 fn fields(&mut self, names: &'static [&'static str]) {
890 self.names = Some(names);
891 }
892
893 fn variant(&mut self, _variant: &deser_core::ser::Variant<'_>) {
894 self.variant = true;
895 }
896}
897
898impl EventSink for Writer {
899 const DESCRIBED: bool = true;
900
901 fn event(
902 &mut self,
903 event: Event<'_>,
904 value: SerializeRef<'_>,
905 state: &mut State,
906 ) -> Result<(), Error> {
907 Writer::event(self, event, value, state)
908 }
909
910 fn pause(&mut self) -> bool {
911 if self.final_until() - self.base < self.limit {
915 return false;
916 }
917 if let Err(err) = self.final_len() {
918 self.error = Some(err);
920 }
921 true
922 }
923}
924
925impl Writer {
926 fn new(config: &SerializerConfig, bytes: BytesFormat) -> Writer {
927 let mut out = String::new();
928 if config.declaration {
929 out.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
930 if config.indent != Indent::None {
931 out.push('\n');
932 }
933 }
934 Writer {
935 config: config.clone(),
936 bytes,
937 out,
938 base: 0,
939 limit: usize::MAX,
940 error: None,
941 stack: Vec::new(),
942 key: None,
943 root_bindings: std::iter::once(("xml", XML_NAMESPACE))
944 .chain(config.names.namespaces.iter().copied())
945 .map(|(prefix, uri)| (prefix.to_string(), uri.to_string()))
946 .collect(),
947 root_declared: false,
948 root_declarations: None,
949 local_bindings: Vec::new(),
950 depth: 0,
951 }
952 }
953
954 fn event(
955 &mut self,
956 event: Event<'_>,
957 value: SerializeRef<'_>,
958 state: &State,
959 ) -> Result<(), Error> {
960 match self.stack.last_mut() {
961 None => self.root(event, value, state)?,
962 Some(Frame::Items { .. }) => self.item(event, value, state)?,
963 Some(Frame::Element(element)) => match self.key.take() {
964 None => {
965 if matches!(event, Event::Atom(_)) && keeps_whitespace(state) {
968 element.lines = Lines::Inline;
969 }
970 self.key(event)?
971 }
972 Some(key) => self.entry(key, event, value, state)?,
973 },
974 }
975 Ok(())
976 }
977
978 fn root(
979 &mut self,
980 event: Event<'_>,
981 value: SerializeRef<'_>,
982 state: &State,
983 ) -> Result<(), Error> {
984 let root = state.event::<RootData>();
988 if let Some(root) = root {
989 self.bind_root(&root.namespaces)?;
990 }
991 let name = match root.and_then(|root| root.name.clone()) {
992 Some(name) => name,
993 None => {
994 let mut name = TypeName::default();
995 value.describe(&mut name);
996 name.0
997 .or_else(|| self.config.root.map(str::to_string))
998 .ok_or_else(|| {
999 Error::new(
1000 ErrorKind::UnsupportedType,
1001 "the name of the root element is unknown (see deser_xml::Root)",
1002 )
1003 })?
1004 }
1005 };
1006 check_element_name(&name)?;
1007 match event {
1008 Event::MapStart(_) => self.open_element(&name, value, state),
1009 Event::Atom(atom) => self.atom_element(&name, &atom, true, state),
1010 _ => Err(Error::new(
1011 ErrorKind::UnsupportedType,
1012 "the root element must be a map or a single value",
1013 )),
1014 }
1015 }
1016
1017 fn bind_root(&mut self, namespaces: &[(String, String)]) -> Result<(), Error> {
1022 let mut bindings = vec![self.root_bindings[0].clone()];
1023 for (prefix, uri) in namespaces {
1024 check_prefix(prefix)?;
1025 if !bindings.iter().any(|(x, _)| x == prefix) {
1026 bindings.push((prefix.clone(), uri.clone()));
1027 }
1028 }
1029 for binding in self.root_bindings.drain(1..) {
1030 if !bindings.iter().any(|(x, _)| *x == binding.0) {
1031 bindings.push(binding);
1032 }
1033 }
1034 self.root_bindings = bindings;
1035 Ok(())
1036 }
1037
1038 fn key(&mut self, event: Event<'_>) -> Result<(), Error> {
1039 let key = match event {
1040 Event::MapEnd => return self.close_element(),
1041 Event::Atom(ref atom) => match self.text(atom)? {
1042 Some(key) => key.into_owned(),
1043 None => return Err(unsupported_key()),
1044 },
1045 _ => return Err(unsupported_key()),
1046 };
1047
1048 let Some(Frame::Element(element)) = self.stack.last_mut() else {
1050 unreachable!()
1051 };
1052 let mut is_last = false;
1053 if let Some((names, cursor)) = &mut element.fields {
1054 match names[*cursor..].iter().position(|name| *name == key) {
1055 Some(offset) => {
1056 *cursor += offset + 1;
1057 if let Attrs::Until(last) = element.attrs {
1058 is_last = *cursor == last + 1;
1059 if *cursor > last + 1 {
1060 element.attrs = Attrs::Done;
1061 }
1062 }
1063 }
1064 None => {
1066 element.fields = None;
1067 if matches!(element.attrs, Attrs::Until(_)) {
1068 element.attrs = Attrs::Unknown;
1069 }
1070 }
1071 }
1072 }
1073
1074 let names = &self.config.names;
1075 self.key = Some(if key == names.text_key {
1076 Key::Text
1077 } else if let Some(name) = attribute_name(names, &key) {
1078 check_element_name(name)?;
1079 Key::Attribute {
1080 name: name.to_string(),
1081 last: is_last,
1082 }
1083 } else {
1084 check_element_name(&key)?;
1085 Key::Element(key)
1086 });
1087 self.settle();
1088 Ok(())
1089 }
1090
1091 fn entry(
1092 &mut self,
1093 key: Key,
1094 event: Event<'_>,
1095 value: SerializeRef<'_>,
1096 state: &State,
1097 ) -> Result<(), Error> {
1098 match (key, event) {
1099 (Key::Attribute { name, last }, Event::Atom(atom)) => {
1100 if let Some(text) = self.text(&atom)? {
1101 let text = text.into_owned();
1102 self.attribute(&name, &text)?;
1103 }
1104 if last && let Some(Frame::Element(element)) = self.stack.last_mut() {
1105 element.attrs = Attrs::Done;
1106 self.settle();
1107 }
1108 Ok(())
1109 }
1110 (Key::Text, Event::Atom(atom)) => {
1111 if let Some(text) = self.text(&atom)? {
1112 let text = text.into_owned();
1113 self.close_start_tag();
1114 if !text.is_empty() {
1115 let Some(Frame::Element(element)) = self.stack.last_mut() else {
1117 unreachable!()
1118 };
1119 element.lines = Lines::Inline;
1120 }
1121 escape(&text, false, &mut self.out)?;
1122 }
1123 Ok(())
1124 }
1125 (Key::Element(name), Event::Atom(atom)) => {
1126 self.atom_element(&name, &atom, false, state)
1127 }
1128 (Key::Element(name), Event::MapStart(_)) => {
1129 self.before_child();
1130 self.open_element(&name, value, state)
1131 }
1132 (Key::Element(name), Event::SeqStart(_)) => {
1133 self.stack.push(Frame::Items {
1134 name,
1135 compact: Layout::of(state) == Layout::Compact,
1136 started: false,
1137 });
1138 Ok(())
1139 }
1140 (Key::Attribute { name, .. }, _) => Err(Error::new(
1141 ErrorKind::UnsupportedType,
1142 format!("attribute `{name}` must be a single value"),
1143 )),
1144 (Key::Text, _) => Err(Error::new(
1145 ErrorKind::UnsupportedType,
1146 "the text of an element must be a single value",
1147 )),
1148 (Key::Element(_), _) => unreachable!("ends are handled by the frames"),
1149 }
1150 }
1151
1152 fn item(
1153 &mut self,
1154 event: Event<'_>,
1155 value: SerializeRef<'_>,
1156 state: &State,
1157 ) -> Result<(), Error> {
1158 let Some(Frame::Items { name, .. }) = self.stack.last() else {
1159 unreachable!()
1160 };
1161 let name = name.clone();
1162 match event {
1163 Event::SeqEnd => {
1164 self.stack.pop();
1165 Ok(())
1166 }
1167 Event::Atom(Atom::Null) => {
1169 self.before_child();
1170 let bindings = self.local_bindings.len();
1171 self.start_tag(&name, false, state)?;
1172 self.out.push_str("/>");
1173 self.local_bindings.truncate(bindings);
1174 Ok(())
1175 }
1176 Event::Atom(atom) => self.atom_element(&name, &atom, false, state),
1177 Event::MapStart(_) => {
1178 self.before_child();
1179 self.open_element(&name, value, state)
1180 }
1181 _ => Err(Error::new(
1182 ErrorKind::UnsupportedType,
1183 "sequences in sequences are not supported",
1184 )),
1185 }
1186 }
1187
1188 fn attribute(&mut self, name: &str, value: &str) -> Result<(), Error> {
1190 let mut declarations = String::new();
1191 let name = self.qualify(name, true, &mut declarations)?;
1192 let Some(Frame::Element(element)) = self.stack.last_mut() else {
1193 unreachable!()
1194 };
1195 let out = if !element.content {
1196 &mut self.out
1197 } else if element.attrs_at >= self.base {
1198 &mut element.late
1199 } else {
1200 return Err(Error::new(
1201 ErrorKind::InvalidValue,
1202 format!(
1203 "attribute `{name}` comes after the start tag of the element was written \
1204 (the fields the value described are not the keys it has)"
1205 ),
1206 ));
1207 };
1208 out.push_str(&declarations);
1209 out.push(' ');
1210 out.push_str(&name);
1211 out.push_str("=\"");
1212 escape(value, true, out)?;
1213 out.push('"');
1214 if !element.content {
1215 element.attrs_at = self.base + self.out.len();
1216 }
1217 self.settle();
1218 Ok(())
1219 }
1220
1221 fn settle(&mut self) {
1224 let Some(Frame::Element(element)) = self.stack.last_mut() else {
1225 return;
1226 };
1227 if !matches!(element.attrs, Attrs::Done) || element.late.is_empty() {
1228 return;
1229 }
1230 let late = std::mem::take(&mut element.late);
1231 let at = element.attrs_at;
1232 self.insert(at, &late);
1233 }
1234
1235 fn insert(&mut self, at: usize, text: &str) {
1239 self.out.insert_str(at - self.base, text);
1240 for frame in &mut self.stack {
1241 if let Frame::Element(element) = frame
1242 && element.attrs_at >= at
1243 {
1244 element.attrs_at += text.len();
1245 }
1246 }
1247 }
1248
1249 fn atom_element(
1253 &mut self,
1254 name: &str,
1255 atom: &Atom<'_>,
1256 is_root: bool,
1257 state: &State,
1258 ) -> Result<(), Error> {
1259 let text = match self.text(atom)? {
1260 Some(text) => text.into_owned(),
1261 None if is_root => String::new(),
1262 None => return Ok(()),
1263 };
1264 self.before_child();
1265 let bindings = self.local_bindings.len();
1266 let name = self.start_tag(name, is_root, state)?;
1267 if text.is_empty() {
1268 self.out.push_str("/>");
1269 } else {
1270 self.out.push('>');
1271 escape(&text, false, &mut self.out)?;
1272 write!(self.out, "</{name}>").unwrap();
1273 }
1274 self.local_bindings.truncate(bindings);
1275 Ok(())
1276 }
1277
1278 fn open_element(
1280 &mut self,
1281 name: &str,
1282 value: SerializeRef<'_>,
1283 state: &State,
1284 ) -> Result<(), Error> {
1285 let bindings = self.local_bindings.len();
1286 let name = self.start_tag(name, self.stack.is_empty(), state)?;
1287 let (fields, attrs) = self.fields_of(value);
1288 let layout = Layout::of(state);
1289 let lines = if self.config.indent == Indent::None
1290 || self.in_line()
1291 || layout == Layout::Compact
1292 || keeps_whitespace(state)
1293 {
1294 Lines::Inline
1295 } else {
1296 Lines::Pending
1297 };
1298 self.stack.push(Frame::Element(Element {
1299 name,
1300 attrs_at: self.base + self.out.len(),
1301 content: false,
1302 late: String::new(),
1303 attrs,
1304 fields: fields.map(|names| (names, 0)),
1305 bindings,
1306 lines,
1307 expanded: layout == Layout::Expanded,
1308 }));
1309 self.depth += 1;
1310 Ok(())
1311 }
1312
1313 fn fields_of(&self, value: SerializeRef<'_>) -> (Option<&'static [&'static str]>, Attrs) {
1316 let mut fields = Fields::default();
1317 value.describe(&mut fields);
1318 let Some(names) = fields.names.filter(|_| !fields.variant) else {
1319 return (None, Attrs::Unknown);
1320 };
1321 let names_config = &self.config.names;
1322 let last = names.iter().rposition(|name| {
1323 *name != names_config.text_key && attribute_name(names_config, name).is_some()
1324 });
1325 let attrs = match last {
1326 Some(last) => Attrs::Until(last),
1327 None => Attrs::Done,
1328 };
1329 (Some(names), attrs)
1330 }
1331
1332 fn in_line(&self) -> bool {
1335 match self.stack.last() {
1336 None => false,
1337 Some(Frame::Items { compact: true, .. }) => true,
1338 Some(Frame::Items { .. }) => match self.stack.iter().rev().nth(1) {
1339 Some(Frame::Element(element)) => element.lines == Lines::Inline,
1340 _ => unreachable!("sequences are in elements"),
1341 },
1342 Some(Frame::Element(element)) => element.lines == Lines::Inline,
1343 }
1344 }
1345
1346 fn before_child(&mut self) {
1349 self.close_start_tag();
1350 if self.config.indent == Indent::None {
1351 return;
1352 }
1353 let text_key = self.config.names.text_key;
1354 let mut frames = self.stack.iter_mut().rev();
1355 let element = match frames.next() {
1356 None => return,
1357 Some(Frame::Items {
1358 compact, started, ..
1359 }) => {
1360 if std::mem::replace(started, true) && *compact {
1362 return;
1363 }
1364 match frames.next() {
1365 Some(Frame::Element(element)) => element,
1366 _ => unreachable!("sequences are in elements"),
1367 }
1368 }
1369 Some(Frame::Element(element)) => element,
1370 };
1371 if element.lines == Lines::Pending {
1372 let text_ahead = !element.expanded
1375 && element
1376 .fields
1377 .is_some_and(|(names, cursor)| names[cursor..].contains(&text_key));
1378 element.lines = if text_ahead {
1379 Lines::Inline
1380 } else {
1381 Lines::Indented
1382 };
1383 }
1384 if element.lines == Lines::Indented {
1385 self.newline(self.depth);
1386 }
1387 }
1388
1389 fn newline(&mut self, depth: usize) {
1391 self.out.push('\n');
1392 let (unit, count) = match self.config.indent {
1393 Indent::None => return,
1394 Indent::Spaces(width) => (' ', width * depth),
1395 Indent::Tab => ('\t', depth),
1396 };
1397 self.out.extend(std::iter::repeat_n(unit, count));
1398 }
1399
1400 fn start_tag(&mut self, name: &str, is_root: bool, state: &State) -> Result<String, Error> {
1406 let mut declarations = String::new();
1407 if !is_root && let Some(Declarations(namespaces)) = state.event::<Declarations>() {
1408 for (prefix, uri) in namespaces {
1409 check_prefix(prefix)?;
1410 if prefix == "xml" {
1411 continue;
1412 }
1413 declare(prefix, uri, &mut declarations)?;
1414 self.local_bindings.push((prefix.clone(), uri.clone()));
1415 }
1416 }
1417 let name = self.qualify(name, false, &mut declarations)?;
1418 self.out.push('<');
1419 self.out.push_str(&name);
1420 if is_root {
1421 self.root_declarations = Some(self.base + self.out.len());
1422 }
1423 self.out.push_str(&declarations);
1424 Ok(name)
1425 }
1426
1427 fn qualify(
1434 &mut self,
1435 name: &str,
1436 is_attribute: bool,
1437 declarations: &mut String,
1438 ) -> Result<String, Error> {
1439 let Some((uri, local)) = split_name(name)? else {
1440 return Ok(name.to_string());
1441 };
1442 let usable = |(prefix, bound): &&(String, String)| {
1445 bound == uri && !(is_attribute && prefix.is_empty())
1446 };
1447 let scoped = &self.local_bindings;
1450 let bound = scoped
1451 .iter()
1452 .enumerate()
1453 .rev()
1454 .find(|(index, binding)| {
1455 usable(binding) && !scoped[index + 1..].iter().any(|(x, _)| *x == binding.0)
1456 })
1457 .map(|(_, binding)| binding)
1458 .or_else(|| {
1459 self.root_bindings
1460 .iter()
1461 .find(|binding| usable(binding) && !scoped.iter().any(|(x, _)| *x == binding.0))
1462 });
1463 let prefix = match bound {
1464 Some((prefix, _)) => prefix,
1465 None => {
1466 let prefix = (0..)
1468 .map(|n| format!("ns{n}"))
1469 .find(|prefix| {
1470 self.root_bindings
1471 .iter()
1472 .chain(&self.local_bindings)
1473 .all(|(x, _)| x != prefix)
1474 })
1475 .unwrap();
1476 let bindings = if self.root_declared {
1477 declare(&prefix, uri, declarations)?;
1478 &mut self.local_bindings
1479 } else {
1480 &mut self.root_bindings
1481 };
1482 bindings.push((prefix, uri.to_string()));
1483 &bindings.last().unwrap().0
1484 }
1485 };
1486 Ok(if prefix.is_empty() {
1487 local.to_string()
1488 } else {
1489 format!("{prefix}:{local}")
1490 })
1491 }
1492
1493 fn declare_root(&mut self) -> Result<(), Error> {
1495 if self.root_declared {
1496 return Ok(());
1497 }
1498 self.root_declared = true;
1499 let Some(at) = self.root_declarations else {
1500 return Ok(());
1501 };
1502 let mut declarations = String::new();
1503 for (prefix, uri) in &self.root_bindings[1..] {
1504 declare(prefix, uri, &mut declarations)?;
1505 }
1506 self.insert(at, &declarations);
1507 Ok(())
1508 }
1509
1510 fn final_until(&self) -> usize {
1512 for frame in &self.stack {
1513 if let Frame::Element(element) = frame
1514 && !matches!(element.attrs, Attrs::Done)
1515 {
1516 return element.attrs_at;
1517 }
1518 }
1519 self.base + self.out.len()
1520 }
1521
1522 fn final_len(&mut self) -> Result<usize, Error> {
1528 let mut until = self.final_until();
1529 if !self.root_declared && self.root_declarations.is_some_and(|at| at < until) {
1531 self.declare_root()?;
1532 until = self.final_until();
1533 }
1534 Ok(until - self.base)
1535 }
1536
1537 fn pass_on(&mut self, out: &mut String) {
1539 let len = self.final_until() - self.base;
1540 out.push_str(&self.out[..len]);
1541 self.out.drain(..len);
1542 self.base += len;
1543 }
1544
1545 fn finish(&mut self) -> Result<(), Error> {
1548 if !self.stack.is_empty() || self.depth > 0 {
1549 return Err(Error::new(ErrorKind::InvalidState, "incomplete document"));
1550 }
1551 self.declare_root()
1552 }
1553
1554 fn close_start_tag(&mut self) {
1556 let element = self.stack.iter_mut().rev().find_map(|frame| match frame {
1557 Frame::Element(element) => Some(element),
1558 Frame::Items { .. } => None,
1559 });
1560 if let Some(element) = element
1561 && !element.content
1562 {
1563 element.content = true;
1564 self.out.push('>');
1565 }
1566 }
1567
1568 fn close_element(&mut self) -> Result<(), Error> {
1569 if let Some(Frame::Element(element)) = self.stack.last_mut() {
1570 element.attrs = Attrs::Done;
1571 }
1572 self.settle();
1573 let Some(Frame::Element(element)) = self.stack.pop() else {
1574 unreachable!()
1575 };
1576 self.depth -= 1;
1577 if element.lines == Lines::Indented {
1578 self.newline(self.depth);
1579 }
1580 if element.content {
1581 write!(self.out, "</{}>", element.name).unwrap();
1582 } else {
1583 self.out.push_str("/>");
1584 }
1585 self.local_bindings.truncate(element.bindings);
1586 Ok(())
1587 }
1588
1589 fn text<'a>(&self, atom: &'a Atom<'_>) -> Result<Option<Cow<'a, str>>, Error> {
1591 Ok(Some(match *atom {
1592 Atom::Null => return Ok(None),
1593 Atom::Implicit(ref value) => {
1596 return Ok(self
1597 .text(&value.value().to_atom())?
1598 .map(|text| Cow::Owned(text.into_owned())));
1599 }
1600 Atom::Bool(value) => Cow::Borrowed(if value { "true" } else { "false" }),
1601 Atom::Str(ref value) | Atom::Lexical(ref value) => Cow::Borrowed(&**value),
1602 Atom::Char(value) => Cow::Owned(value.to_string()),
1603 Atom::U64(value) => Cow::Owned(value.to_string()),
1604 Atom::I64(value) => Cow::Owned(value.to_string()),
1605 Atom::F32(value) => Cow::Owned(float_text(value)),
1606 Atom::F64(value) => Cow::Owned(float_text(value)),
1607 Atom::Bytes(ref bytes) => {
1608 let format = bytes.fallback.copied().unwrap_or(self.bytes);
1609 Cow::Owned(
1610 format
1611 .encode(bytes)
1612 .or_else(|| BytesFormat::BASE64.encode(bytes))
1613 .unwrap_or_default(),
1614 )
1615 }
1616 Atom::Ext(ref ext) => {
1617 if let Some(number) = ext.downcast_value_ref::<Number>() {
1618 Cow::Owned(number.as_str().to_string())
1619 } else if let Some(value) = ext.downcast_ref::<u128>() {
1620 Cow::Owned(value.to_string())
1621 } else if let Some(value) = ext.downcast_ref::<i128>() {
1622 Cow::Owned(value.to_string())
1623 } else {
1624 match ext.fallback() {
1625 Atom::Ext(_) => {
1626 return Err(Error::new(
1627 ErrorKind::UnsupportedType,
1628 format!("XML does not support {}", ext.name()),
1629 ));
1630 }
1631 fallback => match self.text(&fallback)? {
1632 Some(text) => Cow::Owned(text.into_owned()),
1633 None => return Ok(None),
1634 },
1635 }
1636 }
1637 }
1638 _ => {
1639 return Err(Error::new(
1640 ErrorKind::UnsupportedType,
1641 format!("XML does not support {}", atom.name()),
1642 ));
1643 }
1644 }))
1645 }
1646}
1647
1648fn keeps_whitespace(state: &State) -> bool {
1650 state
1651 .event::<KeepsWhitespace>()
1652 .is_some_and(|keeps| keeps.0)
1653}
1654
1655fn float_text<F: zmij::Float + Into<f64>>(value: F) -> String {
1661 let wide: f64 = value.into();
1662 if wide.is_nan() {
1663 "NaN".into()
1664 } else if wide.is_infinite() {
1665 if wide > 0.0 { "INF" } else { "-INF" }.into()
1666 } else {
1667 zmij::Buffer::new().format_finite(value).into()
1668 }
1669}
1670
1671fn escape(text: &str, attribute: bool, out: &mut String) -> Result<(), Error> {
1673 for c in text.chars() {
1674 match c {
1675 '&' => out.push_str("&"),
1676 '<' => out.push_str("<"),
1677 '>' => out.push_str(">"),
1678 '"' if attribute => out.push_str("""),
1679 '\t' if attribute => out.push_str("	"),
1682 '\n' if attribute => out.push_str(" "),
1683 '\r' => out.push_str(" "),
1684 '\t' | '\n' => out.push(c),
1685 c if (c as u32) < 0x20 || c == '\u{fffe}' || c == '\u{ffff}' => {
1686 return Err(Error::new(
1687 ErrorKind::UnsupportedType,
1688 format!("the character {c:?} cannot be written in XML"),
1689 ));
1690 }
1691 c => out.push(c),
1692 }
1693 }
1694 Ok(())
1695}
1696
1697fn attribute_name<'a>(names: &Names, key: &'a str) -> Option<&'a str> {
1701 key.strip_prefix(names.attribute_prefix)
1702 .filter(|_| !names.attribute_prefix.is_empty())
1703}
1704
1705fn declare(prefix: &str, uri: &str, out: &mut String) -> Result<(), Error> {
1707 out.push_str(" xmlns");
1708 if !prefix.is_empty() {
1709 out.push(':');
1710 out.push_str(prefix);
1711 }
1712 out.push_str("=\"");
1713 escape(uri, true, out)?;
1714 out.push('"');
1715 Ok(())
1716}
1717
1718fn split_name(name: &str) -> Result<Option<(&str, &str)>, Error> {
1720 let Some(rest) = name.strip_prefix('{') else {
1721 return Ok(None);
1722 };
1723 match rest.split_once('}') {
1724 Some((uri, local)) if !uri.is_empty() => Ok(Some((uri, local))),
1725 _ => Err(Error::new(
1726 ErrorKind::UnsupportedType,
1727 format!("`{name}` is not a name in XML"),
1728 )),
1729 }
1730}
1731
1732fn check_element_name(name: &str) -> Result<(), Error> {
1735 match split_name(name)? {
1736 Some((_, local)) if local.contains(':') => Err(Error::new(
1737 ErrorKind::UnsupportedType,
1738 format!("`{name}` is not a name in XML"),
1739 )),
1740 Some((_, local)) => check_name(local),
1741 None => check_name(name),
1742 }
1743}
1744
1745fn check_prefix(prefix: &str) -> Result<(), Error> {
1748 if prefix.is_empty() {
1749 return Ok(());
1750 }
1751 check_name(prefix)?;
1752 if prefix.contains(':') {
1753 return Err(Error::new(
1754 ErrorKind::UnsupportedType,
1755 format!("`{prefix}` is not a prefix in XML"),
1756 ));
1757 }
1758 Ok(())
1759}
1760
1761fn check_name(name: &str) -> Result<(), Error> {
1763 let mut chars = name.chars();
1764 let valid = match chars.next() {
1765 Some(c) if c.is_alphabetic() || c == '_' || c == ':' => {
1766 chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | ':' | '-' | '.' | '\u{b7}'))
1767 }
1768 _ => false,
1769 };
1770 if valid {
1771 Ok(())
1772 } else {
1773 Err(Error::new(
1774 ErrorKind::UnsupportedType,
1775 format!("`{name}` is not a name in XML"),
1776 ))
1777 }
1778}
1779
1780#[cold]
1781fn unsupported_key() -> Error {
1782 Error::new(
1783 ErrorKind::UnsupportedType,
1784 "the keys of elements must be names",
1785 )
1786}
1787
1788#[cfg(test)]
1789mod tests {
1790 use std::collections::BTreeMap;
1791
1792 use deser::Serialize;
1793
1794 use super::*;
1795
1796 fn pieces<T: Serialize + ?Sized>(
1799 config: &SerializerConfig,
1800 value: &T,
1801 ) -> Result<Vec<String>, Error> {
1802 let mut pieces = Vec::new();
1803 let mut driver = SerializeDriver::new(&value);
1804 let mut progress = None;
1805 loop {
1806 let mut out = String::new();
1807 let done = config.serialize_part(&mut progress, &mut driver, &mut out, 1)?;
1808 pieces.push(out);
1809 if done {
1810 return Ok(pieces);
1811 }
1812 }
1813 }
1814
1815 #[derive(Serialize)]
1816 struct Feed {
1817 #[deser(rename = "@id")]
1818 id: u32,
1819 title: &'static str,
1820 entry: Vec<Entry>,
1821 }
1822
1823 #[derive(Serialize)]
1824 struct Entry {
1825 #[deser(rename = "$text")]
1826 text: &'static str,
1827 #[deser(rename = "@n")]
1828 n: u32,
1829 #[deser(skip_serializing_if = Option::is_none)]
1830 note: Option<&'static str>,
1831 }
1832
1833 fn feed() -> Feed {
1834 Feed {
1835 id: 1,
1836 title: "t",
1837 entry: vec![
1838 Entry {
1839 text: "a",
1840 n: 1,
1841 note: None,
1842 },
1843 Entry {
1844 text: "b",
1845 n: 2,
1846 note: Some("x"),
1847 },
1848 ],
1849 }
1850 }
1851
1852 #[test]
1853 fn test_structs_stream() {
1854 assert_eq!(
1857 pieces(&SerializerConfig::new(), &feed()).unwrap(),
1858 [
1859 "<Feed id=\"1\"",
1860 "><title>t</title>",
1861 "<entry",
1862 " n=\"1\">a",
1863 "</entry>",
1864 "<entry",
1865 " n=\"2\">b",
1866 "<note>x</note>",
1867 "</entry>",
1868 "</Feed>",
1869 ]
1870 );
1871 }
1872
1873 #[test]
1874 fn test_maps_are_buffered() {
1875 let config = SerializerConfig::builder()
1877 .root("m")
1878 .declaration(true)
1879 .build();
1880 let map = BTreeMap::from([
1881 ("a", BTreeMap::from([("$text", "1"), ("@x", "2")])),
1882 ("b", BTreeMap::from([("$text", "3"), ("@y", "4")])),
1883 ]);
1884 assert_eq!(
1885 pieces(&config, &map).unwrap(),
1886 [
1887 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><m",
1888 "><a x=\"2\">1</a><b y=\"4\">3</b></m>",
1889 ]
1890 );
1891
1892 let map = BTreeMap::from([("$text", "x"), ("@a", "1"), ("b", "2")]);
1893 assert_eq!(
1894 pieces(&config, &map).unwrap(),
1895 [
1896 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><m",
1897 " a=\"1\">x<b>2</b></m>",
1898 ]
1899 );
1900 }
1901
1902 #[test]
1903 fn test_same_output() {
1904 let map = BTreeMap::from([("$text", "x"), ("@a", "1"), ("b", "2")]);
1905 let nested = BTreeMap::from([("a", BTreeMap::from([("@x", "1"), ("b", "2")]))]);
1906 let feed = feed();
1907 let values: [SerializeRef<'_>; 4] = [
1908 SerializeRef::new(&feed),
1909 SerializeRef::new(&map),
1910 SerializeRef::new(&nested),
1911 SerializeRef::new(&Some(42)),
1912 ];
1913 for config in [
1914 SerializerConfig::builder().root("r").build(),
1915 SerializerConfig::builder()
1916 .root("r")
1917 .declaration(true)
1918 .indent(Indent::Spaces(2))
1919 .build(),
1920 ] {
1921 for value in values {
1922 assert_eq!(
1923 pieces(&config, &value).unwrap().concat(),
1924 config.to_string(&value).unwrap()
1925 );
1926 }
1927 }
1928 }
1929
1930 #[test]
1931 fn test_indent_streams() {
1932 let config = SerializerConfig::builder()
1934 .indent(Indent::Spaces(2))
1935 .build();
1936 assert_eq!(
1937 pieces(&config, &feed()).unwrap(),
1938 [
1939 "<Feed id=\"1\"",
1940 ">\n <title>t</title>",
1941 "\n <entry",
1942 " n=\"1\">a",
1943 "</entry>",
1944 "\n <entry",
1945 " n=\"2\">b",
1946 "<note>x</note>",
1947 "</entry>",
1948 "\n</Feed>",
1949 ]
1950 );
1951 }
1952
1953 #[test]
1954 fn test_namespaces_stream() {
1955 #[derive(Serialize)]
1956 #[deser(rename = "{urn:root}root")]
1957 struct Root {
1958 #[deser(rename = "{urn:a}a")]
1959 a: Vec<Child>,
1960 }
1961
1962 #[derive(Serialize)]
1963 struct Child {
1964 #[deser(rename = "@{urn:b}b")]
1965 b: u32,
1966 #[deser(rename = "{urn:a}c")]
1967 c: u32,
1968 }
1969
1970 let root = Root {
1971 a: vec![Child { b: 1, c: 2 }, Child { b: 3, c: 4 }],
1972 };
1973
1974 let config = SerializerConfig::new();
1976 assert_eq!(
1977 config.to_string(&root).unwrap(),
1978 "<ns0:root xmlns:ns0=\"urn:root\" xmlns:ns1=\"urn:a\" xmlns:ns2=\"urn:b\">\
1979 <ns1:a ns2:b=\"1\"><ns1:c>2</ns1:c></ns1:a>\
1980 <ns1:a ns2:b=\"3\"><ns1:c>4</ns1:c></ns1:a></ns0:root>"
1981 );
1982
1983 assert_eq!(
1986 pieces(&config, &root).unwrap().concat(),
1987 "<ns0:root xmlns:ns0=\"urn:root\" xmlns:ns1=\"urn:a\">\
1988 <ns1:a xmlns:ns2=\"urn:b\" ns2:b=\"1\"><ns1:c>2</ns1:c></ns1:a>\
1989 <ns1:a xmlns:ns2=\"urn:b\" ns2:b=\"3\"><ns1:c>4</ns1:c></ns1:a>\
1990 </ns0:root>"
1991 );
1992
1993 let config = SerializerConfig::new().namespaces(&[("r", "urn:root"), ("a", "urn:a")]);
1995 assert_eq!(
1996 pieces(&config, &root).unwrap().concat(),
1997 "<r:root xmlns:r=\"urn:root\" xmlns:a=\"urn:a\">\
1998 <a:a xmlns:ns0=\"urn:b\" ns0:b=\"1\"><a:c>2</a:c></a:a>\
1999 <a:a xmlns:ns0=\"urn:b\" ns0:b=\"3\"><a:c>4</a:c></a:a></r:root>"
2000 );
2001 }
2002
2003 #[test]
2004 fn test_nested_declarations_stream() {
2005 const RESOLVE: crate::DeserializerConfig = crate::DeserializerConfig::builder()
2006 .resolve_namespaces(true)
2007 .build();
2008 let input = r#"<a:r xmlns:a="urn:a"><b xmlns:a="urn:b"><x>1</x></b><a:y>2</a:y></a:r>"#;
2009 let mut value: deser_value::Value = RESOLVE.from_str(input).unwrap();
2010 let config = SerializerConfig::new();
2011 assert_eq!(pieces(&config, &value).unwrap().concat(), input);
2012
2013 let b = value.as_map_mut().unwrap().get_mut("b").unwrap();
2015 b.as_map_mut().unwrap().insert("{urn:a}z", "3");
2016 let xml = config.to_string(&value).unwrap();
2017 assert_eq!(
2018 xml,
2019 "<a:r xmlns:a=\"urn:a\" xmlns:ns0=\"urn:a\"><b xmlns:a=\"urn:b\"><x>1</x>\
2020 <ns0:z>3</ns0:z></b><a:y>2</a:y></a:r>"
2021 );
2022 assert_eq!(pieces(&config, &value).unwrap().concat(), xml);
2023 }
2024
2025 #[test]
2026 fn test_wrong_fields() {
2027 struct Wrong(BTreeMap<&'static str, &'static str>);
2029
2030 impl Serialize for Wrong {
2031 fn describe(_value: &Self, d: &mut dyn Describe) {
2032 d.structure("Wrong");
2033 d.fields(&["$text"]);
2034 }
2035
2036 fn serialize<'a>(
2037 value: &'a Self,
2038 state: &mut deser_core::State,
2039 ) -> Result<deser_core::ser::Emit<'a>, Error> {
2040 BTreeMap::<&str, &str>::serialize(&value.0, state)
2041 }
2042 }
2043
2044 let wrong = Wrong(BTreeMap::from([("$text", "x"), ("@a", "1")]));
2045 let config = SerializerConfig::new();
2046 assert_eq!(
2048 config.to_string(&wrong).unwrap(),
2049 r#"<Wrong a="1">x</Wrong>"#
2050 );
2051 let err = pieces(&config, &wrong).unwrap_err();
2052 assert!(
2053 err.message()
2054 .starts_with("attribute `a` comes after the start tag")
2055 );
2056 }
2057}