1use super::processors::{
6 BlockNodeProcessor, CustomNodeProcessor, InlineNodeProcessor, NodeProcessor,
7};
8#[cfg(feature = "gfm")]
9use crate::ast::TableAlignment;
10use crate::ast::{CodeBlockType, CustomNode, HeadingType, ListItem, Node};
11use crate::error::{WriteError, WriteResult};
12use crate::options::WriterOptions;
13use ecow::EcoString;
14use log;
15use std::fmt::{self};
16
17#[derive(Debug)]
21pub struct CommonMarkWriter {
22 pub options: WriterOptions,
24 buffer: EcoString,
26}
27
28impl CommonMarkWriter {
29 pub fn new() -> Self {
42 Self::with_options(WriterOptions::default())
43 }
44
45 pub fn with_options(options: WriterOptions) -> Self {
66 Self {
67 options,
68 buffer: EcoString::new(),
69 }
70 }
71
72 pub(crate) fn is_strict_mode(&self) -> bool {
74 self.options.strict
75 }
76
77 fn apply_prefix(
89 &self,
90 content: &str,
91 prefix: &str,
92 first_line_prefix: Option<&str>,
93 ) -> EcoString {
94 if content.is_empty() {
95 return EcoString::new();
96 }
97
98 let mut result = EcoString::new();
99 let lines: Vec<&str> = content.lines().collect();
100
101 if !lines.is_empty() {
102 let actual_prefix = first_line_prefix.unwrap_or(prefix);
103 result.push_str(actual_prefix);
104 result.push_str(lines[0]);
105 }
106
107 for line in &lines[1..] {
108 result.push('\n');
109 result.push_str(prefix);
110 result.push_str(line);
111 }
112
113 result
114 }
115
116 pub fn write(&mut self, node: &Node) -> WriteResult<()> {
136 if let Node::Custom(_) = node {
137 return CustomNodeProcessor.process(self, node);
138 }
139
140 if node.is_block() {
141 BlockNodeProcessor.process(self, node)
142 } else if node.is_inline() {
143 InlineNodeProcessor.process(self, node)
144 } else {
145 log::warn!("Unsupported node type encountered and skipped: {:?}", node);
146 Ok(())
147 }
148 }
149
150 #[allow(clippy::borrowed_box)]
152 pub(crate) fn write_custom_node(&mut self, node: &Box<dyn CustomNode>) -> WriteResult<()> {
153 node.write(self)
154 }
155
156 pub(crate) fn get_context_for_node(&self, node: &Node) -> EcoString {
158 match node {
159 Node::Text(_) => "Text".into(),
160 Node::Emphasis(_) => "Emphasis".into(),
161 Node::Strong(_) => "Strong".into(),
162 #[cfg(feature = "gfm")]
163 Node::Strikethrough(_) => "Strikethrough".into(),
164 Node::InlineCode(_) => "InlineCode".into(),
165 Node::Link { .. } => "Link content".into(),
166 Node::Image { .. } => "Image alt text".into(),
167 Node::HtmlElement(_) => "HtmlElement content".into(),
168 Node::Custom(_) => "Custom node".into(),
169 _ => "Unknown inline element".into(),
170 }
171 }
172
173 pub(crate) fn check_no_newline(&self, node: &Node, context: &str) -> WriteResult<()> {
175 if Self::node_contains_newline(node) {
176 if self.is_strict_mode() {
177 return Err(WriteError::NewlineInInlineElement(
178 context.to_string().into(),
179 ));
180 } else {
181 log::warn!(
182 "Newline character found in inline element '{}', but non-strict mode allows it (output may be affected).",
183 context
184 );
185 }
186 }
187 Ok(())
188 }
189
190 fn node_contains_newline(node: &Node) -> bool {
192 match node {
193 Node::Text(s) | Node::InlineCode(s) => s.contains('\n'),
194 Node::Emphasis(children) | Node::Strong(children) => {
195 children.iter().any(Self::node_contains_newline)
196 }
197 #[cfg(feature = "gfm")]
198 Node::Strikethrough(children) => children.iter().any(Self::node_contains_newline),
199 Node::HtmlElement(element) => element.children.iter().any(Self::node_contains_newline),
200 Node::Link { content, .. } => content.iter().any(Self::node_contains_newline),
201 Node::Image { alt, .. } => alt.iter().any(Self::node_contains_newline),
202 Node::SoftBreak | Node::HardBreak => true,
203 Node::Custom(_) => false,
205 _ => false,
206 }
207 }
208
209 pub(crate) fn write_text_content(&mut self, content: &str) -> WriteResult<()> {
211 if self.options.escape_special_chars {
212 let escaped = escape_str::<CommonMarkEscapes>(content);
213 self.write_str(&escaped)?
214 } else {
215 self.write_str(content)?
216 }
217
218 Ok(())
219 }
220
221 pub(crate) fn write_code_content(&mut self, content: &str) -> WriteResult<()> {
223 self.write_char('`')?;
224 self.write_str(content)?;
225 self.write_char('`')?;
226 Ok(())
227 }
228
229 pub(crate) fn write_delimited(&mut self, content: &[Node], delimiter: &str) -> WriteResult<()> {
231 self.write_str(delimiter)?;
232
233 for node in content {
234 self.write(node)?;
235 }
236
237 self.write_str(delimiter)?;
238 Ok(())
239 }
240
241 pub(crate) fn write_document(&mut self, children: &[Node]) -> WriteResult<()> {
243 for (i, child) in children.iter().enumerate() {
244 if i > 0 {
245 self.write_str("\n")?;
246 }
247 self.write(child)?;
248 }
249 Ok(())
250 }
251
252 pub(crate) fn write_heading(
254 &mut self,
255 mut level: u8,
256 content: &[Node],
257 heading_type: &HeadingType,
258 ) -> WriteResult<()> {
259 if level == 0 || level > 6 {
261 if self.is_strict_mode() {
262 return Err(WriteError::InvalidHeadingLevel(level));
263 } else {
264 let original_level = level;
265 level = level.clamp(1, 6); log::warn!(
267 "Invalid heading level: {}. Corrected to {}. Strict mode is off.",
268 original_level,
269 level
270 );
271 }
272 }
273
274 match heading_type {
275 HeadingType::Atx => {
277 for _ in 0..level {
278 self.write_char('#')?;
279 }
280 self.write_char(' ')?;
281
282 for node in content {
283 self.write(node)?;
284 }
285
286 self.write_char('\n')?;
287 }
288
289 HeadingType::Setext => {
290 for node in content {
292 self.write(node)?;
293 }
294 self.write_char('\n')?;
295
296 let underline_char = if level == 1 { '=' } else { '-' };
299
300 let min_len = 3;
303
304 for _ in 0..min_len {
306 self.write_char(underline_char)?;
307 }
308
309 self.write_char('\n')?;
311 }
312 }
313
314 Ok(())
315 }
316
317 pub(crate) fn write_paragraph(&mut self, content: &[Node]) -> WriteResult<()> {
319 if self.options.trim_paragraph_trailing_hard_breaks {
320 let mut last_non_hard_break_index = content.len();
321
322 while last_non_hard_break_index > 0 {
323 if !matches!(content[last_non_hard_break_index - 1], Node::HardBreak) {
324 break;
325 }
326 last_non_hard_break_index -= 1;
327 }
328
329 for node in content.iter().take(last_non_hard_break_index) {
330 self.write(node)?;
331 }
332 } else {
333 for node in content {
334 self.write(node)?;
335 }
336 }
337
338 Ok(())
339 }
340
341 pub(crate) fn write_blockquote(&mut self, content: &[Node]) -> WriteResult<()> {
343 let mut temp_writer = CommonMarkWriter::with_options(self.options.clone());
345
346 for (i, node) in content.iter().enumerate() {
348 if i > 0 {
349 temp_writer.write_char('\n')?;
350 }
351 temp_writer.write(node)?;
353 }
354
355 let all_content = temp_writer.into_string();
357
358 let prefix = "> ";
360 let formatted_content = self.apply_prefix(&all_content, prefix, Some(prefix));
361
362 self.buffer.push_str(&formatted_content);
364 Ok(())
365 }
366
367 pub(crate) fn write_thematic_break(&mut self) -> WriteResult<()> {
369 let char = self.options.thematic_break_char;
370 self.write_str(&format!("{}{}{}", char, char, char))?;
371 Ok(())
372 }
373
374 pub(crate) fn write_code_block(
376 &mut self,
377 language: &Option<EcoString>,
378 content: &str,
379 block_type: &CodeBlockType,
380 ) -> WriteResult<()> {
381 match block_type {
382 CodeBlockType::Indented => {
383 let indent = " ";
384 let indented_content = self.apply_prefix(content, indent, Some(indent));
385 self.buffer.push_str(&indented_content);
386 }
387 CodeBlockType::Fenced => {
388 let max_backticks = content
389 .chars()
390 .fold((0, 0), |(max, current), c| {
391 if c == '`' {
392 (max.max(current + 1), current + 1)
393 } else {
394 (max, 0)
395 }
396 })
397 .0;
398
399 let fence_len = std::cmp::max(max_backticks + 1, 3);
400 let fence = "`".repeat(fence_len);
401
402 self.write_str(&fence)?;
403 if let Some(lang) = language {
404 self.write_str(lang)?;
405 }
406 self.write_char('\n')?;
407
408 self.buffer.push_str(content);
409 if !content.ends_with('\n') {
410 self.write_char('\n')?;
411 }
412
413 self.write_str(&fence)?;
414 }
415 }
416
417 Ok(())
418 }
419
420 pub(crate) fn write_unordered_list(&mut self, items: &[ListItem]) -> WriteResult<()> {
422 let list_marker = self.options.list_marker;
423 let prefix = format!("{} ", list_marker);
424
425 for (i, item) in items.iter().enumerate() {
426 if i > 0 {
427 self.write_char('\n')?;
428 }
429 self.write_list_item(item, &prefix)?;
430 }
431
432 Ok(())
433 }
434
435 pub(crate) fn write_ordered_list(&mut self, start: u32, items: &[ListItem]) -> WriteResult<()> {
437 let mut current_number = start;
439
440 for (i, item) in items.iter().enumerate() {
441 if i > 0 {
442 self.write_char('\n')?;
443 }
444
445 match item {
446 ListItem::Ordered { number, content: _ } => {
448 if let Some(custom_num) = number {
449 let prefix = format!("{}. ", custom_num);
451 self.write_list_item(item, &prefix)?;
452 current_number = custom_num + 1;
454 } else {
455 let prefix = format!("{}. ", current_number);
457 self.write_list_item(item, &prefix)?;
458 current_number += 1;
459 }
460 }
461 _ => {
463 let prefix = format!("{}. ", current_number);
464 self.write_list_item(item, &prefix)?;
465 current_number += 1;
466 }
467 }
468 }
469
470 Ok(())
471 }
472
473 fn write_list_item(&mut self, item: &ListItem, prefix: &str) -> WriteResult<()> {
475 match item {
476 ListItem::Unordered { content } => {
477 self.write_str(prefix)?;
478 self.write_list_item_content(content, prefix.len())?;
479 }
480 ListItem::Ordered { number, content } => {
481 if let Some(num) = number {
482 let custom_prefix = format!("{}. ", num);
483 self.write_str(&custom_prefix)?;
484 self.write_list_item_content(content, custom_prefix.len())?;
485 } else {
486 self.write_str(prefix)?;
487 self.write_list_item_content(content, prefix.len())?;
488 }
489 }
490 #[cfg(feature = "gfm")]
491 ListItem::Task { status, content } => {
492 if self.options.gfm_tasklists {
494 let checkbox = match status {
495 crate::ast::TaskListStatus::Checked => "[x] ",
496 crate::ast::TaskListStatus::Unchecked => "[ ] ",
497 };
498
499 let task_prefix = format!("{}{}", prefix, checkbox);
501 self.write_str(&task_prefix)?;
502 self.write_list_item_content(content, task_prefix.len())?;
503 } else {
504 self.write_str(prefix)?;
506 self.write_list_item_content(content, prefix.len())?;
507 }
508 }
509 }
510
511 Ok(())
512 }
513
514 fn write_list_item_content(&mut self, content: &[Node], prefix_len: usize) -> WriteResult<()> {
516 if content.is_empty() {
517 return Ok(());
518 }
519
520 let mut temp_writer = CommonMarkWriter::with_options(self.options.clone());
521
522 for (i, node) in content.iter().enumerate() {
523 if i > 0 {
524 temp_writer.write_char('\n')?;
525 }
526
527 temp_writer.write(node)?;
528 }
529
530 let all_content = temp_writer.into_string();
531
532 let indent = " ".repeat(prefix_len);
533
534 let formatted_content = self.apply_prefix(&all_content, &indent, Some(""));
535
536 self.buffer.push_str(&formatted_content);
537
538 Ok(())
539 }
540
541 pub(crate) fn write_table(&mut self, headers: &[Node], rows: &[Vec<Node>]) -> WriteResult<()> {
543 self.write_char('|')?;
545 for header in headers {
546 self.check_no_newline(header, "Table Header")?;
547 self.write_char(' ')?;
548 self.write(header)?;
549 self.write_str(" |")?;
550 }
551 self.write_char('\n')?;
552
553 self.write_char('|')?;
555 for _ in 0..headers.len() {
556 self.write_str(" --- |")?;
557 }
558 self.write_char('\n')?;
559
560 for row in rows {
562 self.write_char('|')?;
563 for cell in row {
564 self.check_no_newline(cell, "Table Cell")?;
565 self.write_char(' ')?;
566 self.write(cell)?;
567 self.write_str(" |")?;
568 }
569 self.write_char('\n')?;
570 }
571
572 Ok(())
573 }
574
575 #[cfg(feature = "gfm")]
576 pub(crate) fn write_table_with_alignment(
578 &mut self,
579 headers: &[Node],
580 alignments: &[TableAlignment],
581 rows: &[Vec<Node>],
582 ) -> WriteResult<()> {
583 if !self.options.gfm_tables {
585 return self.write_table(headers, rows);
586 }
587
588 self.write_char('|')?;
590 for header in headers {
591 self.check_no_newline(header, "Table Header")?;
592 self.write_char(' ')?;
593 self.write(header)?;
594 self.write_str(" |")?;
595 }
596 self.write_char('\n')?;
597
598 self.write_char('|')?;
601
602 for i in 0..headers.len() {
604 let alignment = if i < alignments.len() {
605 &alignments[i]
606 } else {
607 &TableAlignment::Center
608 };
609
610 match alignment {
611 TableAlignment::Left => self.write_str(" :--- |")?,
612 TableAlignment::Center => self.write_str(" :---: |")?,
613 TableAlignment::Right => self.write_str(" ---: |")?,
614 TableAlignment::None => self.write_str(" --- |")?,
615 }
616 }
617
618 self.write_char('\n')?;
619
620 for row in rows {
622 self.write_char('|')?;
623 for cell in row {
624 self.check_no_newline(cell, "Table Cell")?;
625 self.write_char(' ')?;
626 self.write(cell)?;
627 self.write_str(" |")?;
628 }
629 self.write_char('\n')?;
630 }
631
632 Ok(())
633 }
634
635 pub(crate) fn write_link(
637 &mut self,
638 url: &str,
639 title: &Option<EcoString>,
640 content: &[Node],
641 ) -> WriteResult<()> {
642 for node in content {
643 self.check_no_newline(node, "Link Text")?;
644 }
645 self.write_char('[')?;
646
647 for node in content {
648 self.write(node)?;
649 }
650
651 self.write_str("](")?;
652 self.write_str(url)?;
653
654 if let Some(title_text) = title {
655 self.write_str(" \"")?;
656 self.write_str(title_text)?;
657 self.write_char('"')?;
658 }
659
660 self.write_char(')')?;
661 Ok(())
662 }
663
664 pub(crate) fn write_image(
666 &mut self,
667 url: &str,
668 title: &Option<EcoString>,
669 alt: &[Node],
670 ) -> WriteResult<()> {
671 for node in alt {
673 self.check_no_newline(node, "Image alt text")?;
674 }
675
676 self.write_str("?;
684 self.write_str(url)?;
685
686 if let Some(title_text) = title {
687 self.write_str(" \"")?;
688 self.write_str(title_text)?;
689 self.write_char('"')?;
690 }
691
692 self.write_char(')')?;
693 Ok(())
694 }
695
696 pub(crate) fn write_soft_break(&mut self) -> WriteResult<()> {
698 self.write_char('\n')?;
699 Ok(())
700 }
701
702 pub(crate) fn write_hard_break(&mut self) -> WriteResult<()> {
704 if self.options.hard_break_spaces {
705 self.write_str(" \n")?;
706 } else {
707 self.write_str("\\\n")?;
708 }
709 Ok(())
710 }
711
712 pub(crate) fn write_html_block(&mut self, content: &str) -> WriteResult<()> {
714 self.buffer.push_str(content);
715
716 Ok(())
717 }
718
719 pub(crate) fn write_autolink(&mut self, url: &str, is_email: bool) -> WriteResult<()> {
721 if url.contains('\n') {
723 if self.is_strict_mode() {
724 return Err(WriteError::NewlineInInlineElement(
725 "Autolink URL".to_string().into(),
726 ));
727 } else {
728 log::warn!(
729 "Newline character found in autolink URL '{}'. Writing it as is, which might result in an invalid link. Strict mode is off.",
730 url
731 );
732 }
734 }
735
736 self.write_char('<')?;
738
739 if !is_email && !url.contains(':') {
742 self.write_str("https://")?;
744 }
745
746 self.write_str(url)?;
747 self.write_char('>')?;
748
749 Ok(())
750 }
751
752 #[cfg(feature = "gfm")]
754 pub(crate) fn write_extended_autolink(&mut self, url: &str) -> WriteResult<()> {
755 if !self.options.gfm_autolinks {
756 self.write_text_content(url)?;
758 return Ok(());
759 }
760
761 if url.contains('\n') {
763 if self.is_strict_mode() {
764 return Err(WriteError::NewlineInInlineElement(
766 "Extended Autolink URL".to_string().into(),
767 ));
768 } else {
769 log::warn!(
770 "Newline character found in extended autolink URL '{}'. Writing it as is, which might result in an invalid link. Strict mode is off.",
771 url
772 );
773 }
775 }
776
777 self.write_str(url)?;
779
780 Ok(())
781 }
782
783 pub(crate) fn write_link_reference_definition(
785 &mut self,
786 label: &str,
787 destination: &str,
788 title: &Option<EcoString>,
789 ) -> WriteResult<()> {
790 self.write_char('[')?;
792 self.write_str(label)?;
793 self.write_str("]: ")?;
794 self.write_str(destination)?;
795
796 if let Some(title_text) = title {
797 self.write_str(" \"")?;
798 self.write_str(title_text)?;
799 self.write_char('"')?;
800 }
801
802 Ok(())
803 }
804
805 pub(crate) fn write_reference_link(
807 &mut self,
808 label: &str,
809 content: &[Node],
810 ) -> WriteResult<()> {
811 for node in content {
813 self.check_no_newline(node, "Reference Link Text")?;
814 }
815
816 if content.is_empty() {
819 self.write_char('[')?;
820 self.write_str(label)?;
821 self.write_char(']')?;
822 return Ok(());
823 }
824
825 let is_shortcut =
827 content.len() == 1 && matches!(&content[0], Node::Text(text) if text == label);
828
829 if is_shortcut {
830 self.write_char('[')?;
832 self.write_str(label)?;
833 self.write_char(']')?;
834 } else {
835 self.write_char('[')?;
837
838 for node in content {
839 self.write(node)?;
840 }
841
842 self.write_str("][")?;
843 self.write_str(label)?;
844 self.write_char(']')?;
845 }
846
847 Ok(())
848 }
849
850 pub(crate) fn write_html_element(
852 &mut self,
853 element: &crate::ast::HtmlElement,
854 ) -> WriteResult<()> {
855 if self.options.strict {
856 if element.tag.contains('<') || element.tag.contains('>') {
857 return Err(WriteError::InvalidHtmlTag(element.tag.clone()));
858 }
859
860 for attr in &element.attributes {
861 if attr.name.contains('<') || attr.name.contains('>') {
862 return Err(WriteError::InvalidHtmlAttribute(attr.name.clone()));
863 }
864 }
865 }
866
867 use crate::writer::html::{HtmlWriter, HtmlWriterOptions};
868
869 let html_options = HtmlWriterOptions {
871 strict: self.options.strict,
873 code_block_language_class_prefix: Some("language-".into()),
875 #[cfg(feature = "gfm")]
876 enable_gfm: self.options.enable_gfm,
877 #[cfg(feature = "gfm")]
878 gfm_disallowed_html_tags: self.options.gfm_disallowed_html_tags.clone(),
879 };
880
881 let mut html_writer = HtmlWriter::with_options(html_options);
882
883 html_writer.write_node(&Node::HtmlElement(element.clone()))?;
884
885 let html_output = html_writer.into_string();
887
888 self.write_str(&html_output)
890 }
891
892 pub fn into_string(self) -> EcoString {
908 self.buffer
909 }
910
911 pub fn write_str(&mut self, s: &str) -> WriteResult<()> {
915 self.buffer.push_str(s);
916 Ok(())
917 }
918
919 pub fn write_char(&mut self, c: char) -> WriteResult<()> {
923 self.buffer.push(c);
924 Ok(())
925 }
926 pub(crate) fn ensure_trailing_newline(&mut self) -> WriteResult<()> {
930 if !self.buffer.ends_with('\n') {
931 self.write_char('\n')?;
932 }
933 Ok(())
934 }
935
936 pub(crate) fn write_emphasis(&mut self, content: &[Node]) -> WriteResult<()> {
938 let delimiter = self.options.emphasis_char.to_string();
939 self.write_delimited(content, &delimiter)
940 }
941
942 pub(crate) fn write_strong(&mut self, content: &[Node]) -> WriteResult<()> {
944 let char = self.options.strong_char;
945 let delimiter = format!("{}{}", char, char);
946 self.write_delimited(content, &delimiter)
947 }
948
949 #[cfg(feature = "gfm")]
951 pub(crate) fn write_strikethrough(&mut self, content: &[Node]) -> WriteResult<()> {
952 if !self.options.enable_gfm || !self.options.gfm_strikethrough {
953 for node in content.iter() {
955 self.write(node)?;
956 }
957 return Ok(());
958 }
959
960 self.write_delimited(content, "~~")
962 }
963}
964
965impl Default for CommonMarkWriter {
966 fn default() -> Self {
967 Self::new()
968 }
969}
970
971impl fmt::Display for Node {
973 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
974 let mut writer = CommonMarkWriter::new();
975 match writer.write(self) {
976 Ok(_) => write!(f, "{}", writer.into_string()),
977 Err(e) => write!(f, "Error writing Node: {}", e),
978 }
979 }
980}
981
982pub(crate) trait Escapes {
984 fn str_needs_escaping(s: &str) -> bool;
986
987 fn char_needs_escaping(c: char) -> bool;
989
990 fn escape_char(c: char) -> Option<&'static str>;
992}
993
994pub(crate) struct CommonMarkEscapes;
996
997impl Escapes for CommonMarkEscapes {
998 fn str_needs_escaping(s: &str) -> bool {
999 s.chars().any(Self::char_needs_escaping)
1000 }
1001
1002 fn char_needs_escaping(c: char) -> bool {
1003 matches!(c, '\\' | '*' | '_' | '[' | ']' | '<' | '>' | '`')
1004 }
1005
1006 fn escape_char(c: char) -> Option<&'static str> {
1007 match c {
1008 '\\' => Some(r"\\"),
1009 '*' => Some(r"\*"),
1010 '_' => Some(r"\_"),
1011 '[' => Some(r"\["),
1012 ']' => Some(r"\]"),
1013 '<' => Some(r"\<"),
1014 '>' => Some(r"\>"),
1015 '`' => Some(r"\`"),
1016 _ => None,
1017 }
1018 }
1019}
1020
1021pub(crate) struct Escaped<'a, E: Escapes> {
1023 inner: &'a str,
1024 _phantom: std::marker::PhantomData<E>,
1025}
1026
1027impl<'a, E: Escapes> Escaped<'a, E> {
1028 pub fn new(s: &'a str) -> Self {
1030 Self {
1031 inner: s,
1032 _phantom: std::marker::PhantomData,
1033 }
1034 }
1035}
1036
1037impl<E: Escapes> std::fmt::Display for Escaped<'_, E> {
1038 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1039 for c in self.inner.chars() {
1040 if E::char_needs_escaping(c) {
1041 f.write_str(E::escape_char(c).unwrap())?;
1042 } else {
1043 write!(f, "{}", c)?;
1044 }
1045 }
1046 Ok(())
1047 }
1048}
1049
1050pub(crate) fn escape_str<E: Escapes>(s: &str) -> std::borrow::Cow<'_, str> {
1052 if E::str_needs_escaping(s) {
1053 std::borrow::Cow::Owned(format!("{}", Escaped::<E>::new(s)))
1054 } else {
1055 std::borrow::Cow::Borrowed(s)
1056 }
1057}