Skip to main content

markdown_gen/markdown/
mod.rs

1use std::io;
2use std::io::{Error, Write};
3use Escaping::{InlineCode, Normal};
4
5#[cfg(test)]
6mod tests;
7
8/// Specifies string escaping mode
9#[derive(Clone, Copy)]
10pub enum Escaping {
11    /// `` \`*_{}[]()#+-.!`` will be escaped with a backslash
12    Normal,
13    /// Inline code will be surrounded by enough backticks to escape the contents
14    InlineCode,
15}
16
17/// Struct for generating Markdown
18pub struct Markdown<W: Write> {
19    writer: W,
20}
21
22impl<W: Write> Markdown<W> {
23    /// Creates a new [Markdown](struct.Markdown.html) struct
24    ///
25    /// # Arguments
26    ///
27    /// * `writer` - Destination for Markdown data
28    pub fn new(writer: W) -> Self {
29        Self { writer }
30    }
31
32    /// Returns the underlying `writer` and consumes the object
33    pub fn into_inner(self) -> W {
34        self.writer
35    }
36
37    /// Writes a [MarkdownWritable](trait.MarkdownWritable.html) to the document
38    ///
39    /// # Returns
40    /// `()` or `std::io::Error` if an error occurred during writing to the underlying writer
41    pub fn write<T: MarkdownWritable>(&mut self, element: T) -> Result<(), io::Error> {
42        element.write_to(&mut self.writer, false, Normal, None)?;
43        Ok(())
44    }
45}
46
47/// Trait for objects writable to Markdown documents
48pub trait MarkdownWritable {
49    /// Writes `self` as markdown to `writer`
50    ///
51    /// # Arguments
52    /// * `writer` - Destination writer
53    /// * `inner` - `true` if element is inside another element, `false` otherwise
54    /// * `escape` - Mode used for escaping string
55    /// * `line_prefix` - Prefix written before each line
56    ///
57    /// # Returns
58    /// `()` or `std::io::Error` if an error occurred during writing
59    fn write_to(
60        &self,
61        writer: &mut dyn Write,
62        inner: bool,
63        escape: Escaping,
64        line_prefix: Option<&[u8]>,
65    ) -> Result<(), io::Error>;
66
67    /// Counts length of longest streak of `char` in `self`
68    ///
69    /// # Arguments
70    /// * `char` - Character to search for
71    /// * `carry` - Length to add to possible occurrence at the beginning
72    ///
73    /// # Returns
74    /// `(count, carry)`
75    /// * `count` - Length of longest streak
76    /// * `carry` - Length of streak at the end
77    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize);
78}
79
80/// Trait for objects convertible to a Markdown element
81pub trait AsMarkdown<'a> {
82    /// Converts `self` to [Paragraph](struct.Paragraph.html)
83    fn paragraph(self) -> Paragraph<'a>;
84    /// Converts `self` to [Heading](struct.Heading.html)
85    ///
86    /// # Arguments
87    /// * `level` - Heading level (1-6)
88    fn heading(self, level: usize) -> Heading<'a>;
89    /// Converts `self` to [Link](struct.Link.html)
90    ///
91    /// # Arguments
92    /// * `address` - Address which will the link lead to
93    fn link_to(self, address: &'a str) -> Link<'a>;
94
95    /// Converts `self` to **bold** [RichText](struct.RichText.html)
96    fn bold(self) -> RichText<'a>;
97
98    /// Converts `self` to *italic* [RichText](struct.RichText.html)
99    fn italic(self) -> RichText<'a>;
100
101    /// Converts `self` to `code` [RichText](struct.RichText.html)
102    fn code(self) -> RichText<'a>;
103
104    /// Converts `self` to [Quote](struct.Quote.html)
105    fn quote(self) -> Quote<'a>;
106}
107
108//region Paragraph
109/// Markdown paragraph
110pub struct Paragraph<'a> {
111    children: Vec<Box<dyn 'a + MarkdownWritable>>,
112}
113
114impl<'a> Paragraph<'a> {
115    /// Creates an empty paragraph
116    pub fn new() -> Self {
117        Self {
118            children: Vec::new(),
119        }
120    }
121
122    /// Appends an element to the paragraph
123    pub fn append<T: 'a + MarkdownWritable>(mut self, element: T) -> Self {
124        self.children.push(Box::new(element));
125        self
126    }
127}
128
129impl MarkdownWritable for &'_ Paragraph<'_> {
130    fn write_to(
131        &self,
132        writer: &mut dyn Write,
133        inner: bool,
134        escape: Escaping,
135        line_prefix: Option<&[u8]>,
136    ) -> Result<(), Error> {
137        for child in &self.children {
138            child.write_to(writer, true, escape, line_prefix)?;
139        }
140        if !inner {
141            write_line_prefixed(writer, b"\n\n", line_prefix)?;
142        }
143        Ok(())
144    }
145
146    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
147        let mut carry = carry;
148        let mut count = 0;
149        for child in &self.children {
150            let (c, cr) = child.count_max_streak(char, carry);
151            count += c;
152            carry = cr;
153        }
154        count += carry;
155        (count, 0)
156    }
157}
158
159impl MarkdownWritable for Paragraph<'_> {
160    fn write_to(
161        &self,
162        writer: &mut dyn Write,
163        inner: bool,
164        escape: Escaping,
165        line_prefix: Option<&[u8]>,
166    ) -> Result<(), Error> {
167        (&self).write_to(writer, inner, escape, line_prefix)
168    }
169
170    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
171        (&self).count_max_streak(char, carry)
172    }
173}
174//endregion
175
176//region Heading
177/// Markdown heading
178pub struct Heading<'a> {
179    children: Vec<Box<dyn 'a + MarkdownWritable>>,
180    level: usize,
181}
182
183impl<'a> Heading<'a> {
184    /// Creates an empty heading
185    ///
186    /// # Arguments
187    /// * `level` - Heading level (1-6)
188    pub fn new(level: usize) -> Self {
189        assert!(level > 0 && level <= 6, "Heading level must be range 1-6.");
190        Self {
191            children: Vec::new(),
192            level,
193        }
194    }
195
196    /// Appends an element to the heading
197    pub fn append<T: 'a + MarkdownWritable>(mut self, element: T) -> Self {
198        self.children.push(Box::new(element));
199        self
200    }
201}
202
203impl MarkdownWritable for &'_ Heading<'_> {
204    fn write_to(
205        &self,
206        writer: &mut dyn Write,
207        inner: bool,
208        _escape: Escaping,
209        line_prefix: Option<&[u8]>,
210    ) -> Result<(), Error> {
211        assert!(!inner, "Inner headings are forbidden.");
212        let mut prefix = Vec::new();
213        prefix.resize(self.level, b'#');
214        prefix.push(b' ');
215        writer.write_all(&prefix)?;
216        for child in &self.children {
217            child.write_to(writer, true, Normal, line_prefix)?;
218        }
219        write_line_prefixed(writer, b"\n", line_prefix)?;
220        Ok(())
221    }
222
223    fn count_max_streak(&self, char: u8, _carry: usize) -> (usize, usize) {
224        let mut carry = 0;
225        let mut count = 0;
226        for child in &self.children {
227            let (c, cr) = child.count_max_streak(char, carry);
228            count += c;
229            carry = cr;
230        }
231        (count, carry)
232    }
233}
234
235impl MarkdownWritable for Heading<'_> {
236    fn write_to(
237        &self,
238        writer: &mut dyn Write,
239        inner: bool,
240        escape: Escaping,
241        line_prefix: Option<&[u8]>,
242    ) -> Result<(), Error> {
243        (&self).write_to(writer, inner, escape, line_prefix)
244    }
245
246    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
247        (&self).count_max_streak(char, carry)
248    }
249}
250//endregion
251
252//region Link
253/// Markdown link
254pub struct Link<'a> {
255    children: Vec<Box<dyn 'a + MarkdownWritable>>,
256    address: &'a str,
257}
258
259impl<'a> Link<'a> {
260    /// Creates an empty link, which leads to `address`
261    pub fn new(address: &'a str) -> Self {
262        Self {
263            children: Vec::new(),
264            address,
265        }
266    }
267
268    /// Appends an element to the link's text
269    pub fn append<T: 'a + MarkdownWritable>(mut self, element: T) -> Self {
270        self.children.push(Box::new(element));
271        self
272    }
273}
274
275impl MarkdownWritable for &'_ Link<'_> {
276    fn write_to(
277        &self,
278        writer: &mut dyn Write,
279        inner: bool,
280        escape: Escaping,
281        line_prefix: Option<&[u8]>,
282    ) -> Result<(), Error> {
283        writer.write_all(b"[")?;
284        for child in &self.children {
285            child.write_to(writer, true, escape, line_prefix)?;
286        }
287        writer.write_all(b"](")?;
288        self.address.write_to(writer, true, escape, line_prefix)?;
289        writer.write_all(b")")?;
290        if !inner {
291            write_line_prefixed(writer, b"\n", line_prefix)?;
292        }
293        Ok(())
294    }
295
296    fn count_max_streak(&self, char: u8, _carry: usize) -> (usize, usize) {
297        let (mut addr, addr_cr) = self.address.count_max_streak(char, 0);
298        addr += addr_cr;
299        let mut carry = 0;
300        let mut count = 0;
301        for child in &self.children {
302            let (c, cr) = child.count_max_streak(char, carry);
303            count += c;
304            carry = cr;
305        }
306        count += carry;
307        return if count > addr { (count, 0) } else { (addr, 0) };
308    }
309}
310
311impl MarkdownWritable for Link<'_> {
312    fn write_to(
313        &self,
314        writer: &mut dyn Write,
315        inner: bool,
316        escape: Escaping,
317        line_prefix: Option<&[u8]>,
318    ) -> Result<(), Error> {
319        (&self).write_to(writer, inner, escape, line_prefix)
320    }
321
322    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
323        (&self).count_max_streak(char, carry)
324    }
325}
326
327impl<'a> AsMarkdown<'a> for &'a Link<'a> {
328    fn paragraph(self) -> Paragraph<'a> {
329        Paragraph::new().append(self)
330    }
331
332    fn heading(self, level: usize) -> Heading<'a> {
333        Heading::new(level).append(self)
334    }
335
336    fn link_to(self, _address: &'a str) -> Link<'a> {
337        panic!("Link cannot contain another link.");
338    }
339
340    fn bold(self) -> RichText<'a> {
341        panic!("Cannot change link's body. Please use 'x.as_bold().as_link_to(...);'");
342    }
343
344    fn italic(self) -> RichText<'a> {
345        panic!("Cannot change link's body. Please use 'x.as_italic().as_link_to(...);'");
346    }
347
348    fn code(self) -> RichText<'a> {
349        panic!("Cannot change link's body. Please use 'x.as_code().as_link_to(...);'");
350    }
351
352    fn quote(self) -> Quote<'a> {
353        Quote::new().append(self)
354    }
355}
356
357impl<'a> AsMarkdown<'a> for Link<'a> {
358    fn paragraph(self) -> Paragraph<'a> {
359        Paragraph::new().append(self)
360    }
361
362    fn heading(self, level: usize) -> Heading<'a> {
363        Heading::new(level).append(self)
364    }
365
366    fn link_to(self, _address: &'a str) -> Link<'a> {
367        panic!("Link cannot contain another link.");
368    }
369
370    fn bold(self) -> RichText<'a> {
371        panic!("Cannot change link's body. Please use 'x.as_bold().as_link_to(...);'");
372    }
373
374    fn italic(self) -> RichText<'a> {
375        panic!("Cannot change link's body. Please use 'x.as_italic().as_link_to(...);'");
376    }
377
378    fn code(self) -> RichText<'a> {
379        panic!("Cannot change link's body. Please use 'x.as_code().as_link_to(...);'");
380    }
381
382    fn quote(self) -> Quote<'a> {
383        Quote::new().append(self)
384    }
385}
386//endregion
387
388//region RichText
389/// Text styled with **bold**, *italic* or `code`
390#[derive(Copy, Clone)]
391pub struct RichText<'a> {
392    bold: bool,
393    italic: bool,
394    code: bool,
395    text: &'a str,
396}
397
398impl<'a> RichText<'a> {
399    fn new(text: &'a str) -> Self {
400        Self {
401            bold: false,
402            italic: false,
403            code: false,
404            text,
405        }
406    }
407}
408
409impl MarkdownWritable for &'_ RichText<'_> {
410    fn write_to(
411        &self,
412        writer: &mut dyn Write,
413        inner: bool,
414        mut escape: Escaping,
415        line_prefix: Option<&[u8]>,
416    ) -> Result<(), Error> {
417        let mut symbol = Vec::new();
418        if self.bold {
419            symbol.extend_from_slice(b"**");
420        }
421        if self.italic {
422            symbol.push(b'*');
423        }
424        if self.code {
425            let (mut ticks_needed, carry) = self.text.count_max_streak(b'`', 0);
426            ticks_needed += 1 + carry;
427            symbol.extend(vec![b'`'; ticks_needed]);
428            symbol.push(b' ');
429            escape = InlineCode;
430        }
431
432        writer.write_all(&symbol)?;
433        self.text.write_to(writer, true, escape, line_prefix)?;
434        symbol.reverse();
435        writer.write_all(&symbol)?;
436
437        if !inner {
438            write_line_prefixed(writer, b"\n\n", line_prefix)?;
439        }
440        Ok(())
441    }
442
443    fn count_max_streak(&self, char: u8, _carry: usize) -> (usize, usize) {
444        let (res, cr) = self.text.count_max_streak(char, 0);
445        (res + cr, 0)
446    }
447}
448
449impl MarkdownWritable for RichText<'_> {
450    fn write_to(
451        &self,
452        writer: &mut dyn Write,
453        inner: bool,
454        escape: Escaping,
455        line_prefix: Option<&[u8]>,
456    ) -> Result<(), Error> {
457        (&self).write_to(writer, inner, escape, line_prefix)
458    }
459
460    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
461        (&self).count_max_streak(char, carry)
462    }
463}
464
465impl<'a> AsMarkdown<'a> for &'a RichText<'a> {
466    fn paragraph(self) -> Paragraph<'a> {
467        Paragraph::new().append(self)
468    }
469
470    fn heading(self, level: usize) -> Heading<'a> {
471        Heading::new(level).append(self)
472    }
473
474    fn link_to(self, address: &'a str) -> Link<'a> {
475        Link::new(address).append(self)
476    }
477
478    fn bold(self) -> RichText<'a> {
479        let mut clone = *self;
480        clone.bold = true;
481        *self
482    }
483
484    fn italic(self) -> RichText<'a> {
485        let mut clone = *self;
486        clone.italic = true;
487        *self
488    }
489
490    fn code(self) -> RichText<'a> {
491        let mut clone = *self;
492        clone.code = true;
493        *self
494    }
495
496    fn quote(self) -> Quote<'a> {
497        Quote::new().append(self)
498    }
499}
500
501impl<'a> AsMarkdown<'a> for RichText<'a> {
502    fn paragraph(self) -> Paragraph<'a> {
503        Paragraph::new().append(self)
504    }
505
506    fn heading(self, level: usize) -> Heading<'a> {
507        Heading::new(level).append(self)
508    }
509
510    fn link_to(self, address: &'a str) -> Link<'a> {
511        Link::new(address).append(self)
512    }
513
514    fn bold(mut self) -> RichText<'a> {
515        self.bold = true;
516        self
517    }
518
519    fn italic(mut self) -> RichText<'a> {
520        self.italic = true;
521        self
522    }
523
524    fn code(mut self) -> RichText<'a> {
525        self.code = true;
526        self
527    }
528
529    fn quote(self) -> Quote<'a> {
530        Quote::new().append(self)
531    }
532}
533//endregion
534
535//region List
536/// Bulleted or numbered list
537pub struct List<'a> {
538    title: Vec<Box<dyn 'a + MarkdownWritable>>,
539    items: Vec<Box<dyn 'a + MarkdownWritable>>,
540    numbered: bool,
541}
542
543impl<'a> List<'a> {
544    /// Creates an empty list
545    /// # Arguments
546    /// * `numbered` - `true` for numbered list, `false` for bulleted list
547    pub fn new(numbered: bool) -> Self {
548        Self {
549            items: Vec::new(),
550            title: Vec::new(),
551            numbered,
552        }
553    }
554
555    /// Append an item to the list title
556    pub fn title<T: 'a + MarkdownWritable>(mut self, item: T) -> Self {
557        self.title.push(Box::new(item));
558        self
559    }
560
561    /// Adds an item to the list
562    pub fn item<T: 'a + MarkdownWritable>(mut self, item: T) -> Self {
563        self.items.push(Box::new(item));
564        self
565    }
566}
567
568impl MarkdownWritable for &'_ List<'_> {
569    fn write_to(
570        &self,
571        writer: &mut dyn Write,
572        _inner: bool,
573        escape: Escaping,
574        line_prefix: Option<&[u8]>,
575    ) -> Result<(), Error> {
576        for it in &self.title {
577            it.write_to(writer, true, escape, line_prefix)?;
578        }
579        let mut prefix = Vec::new();
580        if line_prefix.is_some() {
581            prefix.extend_from_slice(line_prefix.unwrap());
582        }
583        prefix.extend_from_slice(b"   ");
584
585        for it in &self.items {
586            if self.numbered {
587                write_line_prefixed(writer, b"\n1. ", Some(&prefix))?;
588            } else {
589                write_line_prefixed(writer, b"\n* ", Some(&prefix))?;
590            }
591
592            it.write_to(writer, true, escape, Some(&prefix))?;
593        }
594        Ok(())
595    }
596
597    fn count_max_streak(&self, char: u8, _carry: usize) -> (usize, usize) {
598        let mut count = 0;
599        for child in &self.items {
600            let (c, _) = child.count_max_streak(char, 0);
601            if c > count {
602                count = c;
603            }
604        }
605        (count, 0)
606    }
607}
608
609impl<'a> MarkdownWritable for List<'a> {
610    fn write_to(
611        &self,
612        writer: &mut dyn Write,
613        inner: bool,
614        escape: Escaping,
615        line_prefix: Option<&[u8]>,
616    ) -> Result<(), Error> {
617        (&self).write_to(writer, inner, escape, line_prefix)
618    }
619
620    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
621        (&self).count_max_streak(char, carry)
622    }
623}
624
625impl<'a> AsMarkdown<'a> for List<'a> {
626    fn paragraph(self) -> Paragraph<'a> {
627        Paragraph::new().append(self)
628    }
629
630    fn heading(self, _level: usize) -> Heading<'a> {
631        panic!("Cannot make a Heading from List");
632    }
633
634    fn link_to(self, _address: &'a str) -> Link<'a> {
635        panic!("Cannot make a Link from List");
636    }
637
638    fn bold(self) -> RichText<'a> {
639        panic!("Cannot make a List bold");
640    }
641
642    fn italic(self) -> RichText<'a> {
643        panic!("Cannot make a List italic");
644    }
645
646    fn code(self) -> RichText<'a> {
647        panic!("Cannot make a List code");
648    }
649
650    fn quote(self) -> Quote<'a> {
651        Quote::new().append(self)
652    }
653}
654//endregion
655
656//region Quote
657/// A quote block
658pub struct Quote<'a> {
659    children: Vec<Box<dyn 'a + MarkdownWritable>>,
660}
661
662impl<'a> Quote<'a> {
663    /// Creates an empty quote block
664    fn new() -> Self {
665        Self {
666            children: Vec::new(),
667        }
668    }
669
670    /// Appends an element to the quote block
671    pub fn append<T: 'a + MarkdownWritable>(mut self, element: T) -> Self {
672        self.children.push(Box::new(element));
673        self
674    }
675}
676
677impl MarkdownWritable for &'_ Quote<'_> {
678    fn write_to(
679        &self,
680        writer: &mut dyn Write,
681        inner: bool,
682        escape: Escaping,
683        line_prefix: Option<&[u8]>,
684    ) -> Result<(), Error> {
685        let mut prefix = Vec::new();
686        if line_prefix.is_some() {
687            prefix.extend_from_slice(line_prefix.unwrap());
688        }
689        prefix.extend_from_slice(b">");
690        if !inner {
691            write_line_prefixed(writer, b"\n", line_prefix)?;
692        }
693        writer.write_all(b">")?;
694        for child in &self.children {
695            child.write_to(writer, true, escape, Some(&prefix))?;
696        }
697        if !inner {
698            write_line_prefixed(writer, b"\n\n", line_prefix)?;
699        }
700
701        Ok(())
702    }
703
704    fn count_max_streak(&self, char: u8, _carry: usize) -> (usize, usize) {
705        let mut count = 0;
706        for child in &self.children {
707            let (c, _) = child.count_max_streak(char, 0);
708            if c > count {
709                count = c;
710            }
711        }
712        (count, 0)
713    }
714}
715impl<'a> MarkdownWritable for Quote<'a> {
716    fn write_to(
717        &self,
718        writer: &mut dyn Write,
719        inner: bool,
720        escape: Escaping,
721        line_prefix: Option<&[u8]>,
722    ) -> Result<(), Error> {
723        (&self).write_to(writer, inner, escape, line_prefix)
724    }
725
726    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
727        (&self).count_max_streak(char, carry)
728    }
729}
730//endregion
731
732//region String and &str
733impl MarkdownWritable for &str {
734    fn write_to(
735        &self,
736        writer: &mut dyn Write,
737        inner: bool,
738        escape: Escaping,
739        line_prefix: Option<&[u8]>,
740    ) -> Result<(), Error> {
741        match escape {
742            Normal => {
743                write_escaped(writer, self.as_bytes(), b"\\`*_{}[]()#+-.!", line_prefix)?;
744            }
745            InlineCode => {
746                writer.write_all(self.as_bytes())?;
747            }
748        }
749        if !inner {
750            write_line_prefixed(writer, b"\n\n", line_prefix)?;
751        }
752        Ok(())
753    }
754
755    fn count_max_streak(&self, char: u8, carry: usize) -> (usize, usize) {
756        let mut iter = self.as_bytes().iter();
757        let mut max = 0;
758        let mut current = carry;
759        loop {
760            match iter.next() {
761                None => {
762                    break;
763                }
764                Some(ch) => {
765                    if *ch == char {
766                        current += 1;
767                    } else {
768                        if current > max {
769                            max = current;
770                        }
771                        current = 0;
772                    }
773                }
774            }
775        }
776        (max, current)
777    }
778}
779
780impl<'a> AsMarkdown<'a> for &'a String {
781    fn paragraph(self) -> Paragraph<'a> {
782        self.as_str().paragraph()
783    }
784
785    fn heading(self, level: usize) -> Heading<'a> {
786        self.as_str().heading(level)
787    }
788
789    fn link_to(self, address: &'a str) -> Link<'a> {
790        self.as_str().link_to(address)
791    }
792
793    fn bold(self) -> RichText<'a> {
794        self.as_str().bold()
795    }
796
797    fn italic(self) -> RichText<'a> {
798        self.as_str().italic()
799    }
800
801    fn code(self) -> RichText<'a> {
802        self.as_str().code()
803    }
804
805    fn quote(self) -> Quote<'a> {
806        self.as_str().quote()
807    }
808}
809
810impl<'a> AsMarkdown<'a> for &'a str {
811    fn paragraph(self) -> Paragraph<'a> {
812        Paragraph::new().append(self)
813    }
814
815    fn heading(self, level: usize) -> Heading<'a> {
816        Heading::new(level).append(self)
817    }
818
819    fn link_to(self, address: &'a str) -> Link<'a> {
820        Link::new(address).append(self)
821    }
822
823    fn bold(self) -> RichText<'a> {
824        RichText::new(self).bold()
825    }
826
827    fn italic(self) -> RichText<'a> {
828        RichText::new(self).italic()
829    }
830
831    fn code(self) -> RichText<'a> {
832        RichText::new(self).code()
833    }
834
835    fn quote(self) -> Quote<'a> {
836        Quote::new().append(self)
837    }
838}
839//endregion
840
841fn write_escaped<W: Write + ?Sized>(
842    writer: &mut W,
843    mut data: &[u8],
844    escape: &[u8],
845    line_prefix: Option<&[u8]>,
846) -> Result<(), Error> {
847    loop {
848        let slice_at = data.iter().position(|x| escape.contains(x));
849        match slice_at {
850            Option::None => {
851                write_line_prefixed(writer, &data, line_prefix)?;
852                return Ok(());
853            }
854            Some(slice_at) => {
855                write_line_prefixed(writer, &data[..slice_at], line_prefix)?;
856                writer.write_all(b"\\")?;
857                write_line_prefixed(writer, &data[slice_at..slice_at + 1], line_prefix)?;
858                data = &data[slice_at + 1..];
859            }
860        }
861    }
862}
863
864fn write_line_prefixed<W: Write + ?Sized>(
865    writer: &mut W,
866    mut data: &[u8],
867    line_prefix: Option<&[u8]>,
868) -> Result<(), Error> {
869    match line_prefix {
870        None => {
871            writer.write_all(data)?;
872        }
873        Some(line_prefix) => loop {
874            let slice_at = data.iter().position(|x| *x == b'\n');
875            match slice_at {
876                Option::None => {
877                    writer.write_all(&data)?;
878                    break;
879                }
880                Some(slice_at) => {
881                    writer.write_all(&data[..slice_at + 1])?;
882                    writer.write_all(line_prefix)?;
883                    data = &data[slice_at + 1..];
884                }
885            }
886        },
887    }
888
889    Ok(())
890}