parse_hyperlinks/
renderer.rs

1//! A set of functions providing markup source code to HTML renderer, that make
2//! hyperlinks clickable.
3
4use crate::iterator::MarkupLink;
5use crate::parser::Link;
6use html_escape::encode_double_quoted_attribute;
7use html_escape::encode_text;
8use std::borrow::Cow;
9use std::io;
10use std::io::Write;
11
12fn render<'a, O, P, W>(
13    input: &'a str,
14    begin_doc: &str,
15    end_doc: &str,
16    verb_renderer: O,
17    link_renderer: P,
18    render_label: bool,
19    output: &mut W,
20) -> Result<(), io::Error>
21where
22    O: Fn(Cow<'a, str>) -> Cow<'a, str>,
23    P: Fn((Cow<'a, str>, Link<'a>)) -> String,
24    W: Write,
25{
26    // As this will be overwritten inside the loop, the first value only counts
27    // when there are no hyperlinks in the input. In this case we print the
28    // input as a whole.
29    let mut rest = Cow::Borrowed(input);
30
31    output.write_all(begin_doc.as_bytes())?;
32    for ((skipped2, consumed2, remaining2), link) in MarkupLink::new(input, render_label) {
33        // (text2, dest2, title2)
34        let skipped = encode_text(skipped2);
35        let consumed = encode_text(consumed2);
36        let remaining = encode_text(remaining2);
37        output.write_all(verb_renderer(skipped).as_bytes())?;
38        let rendered_link = link_renderer((consumed, link));
39        output.write_all(rendered_link.as_bytes())?;
40        rest = remaining;
41    }
42    output.write_all(verb_renderer(rest).as_bytes())?;
43    output.write_all(end_doc.as_bytes())?;
44    Ok(())
45}
46
47/// # Source code viewer with link renderer
48///
49/// Text to HTML renderer that prints the input text “as it is”, but
50/// renders links with markup. Links are clickable and only their
51/// _link text_ is shown (the part enclosed with `<a>` and `</a>`).
52///
53/// ## Markdown
54/// ```
55/// use parse_hyperlinks::renderer::text_links2html;
56/// use std::borrow::Cow;
57///
58/// let i = r#"abc[text0](dest0 "title0")abc
59/// abc[text1][label1]abc
60/// abc[text2](dest2 "title2")abc
61/// [text3]: dest3 "title3"
62/// [label1]: dest1 "title1"
63/// abc[text3]abc
64/// abc<foo@dest4>abc
65/// abc![alt5](dest5)abc
66/// abc[![alt6](src6)](dest6)abc
67/// "#;
68///
69/// let expected = "\
70/// <pre>abc<a href=\"dest0\" title=\"title0\">text0</a>abc
71/// abc<a href=\"dest1\" title=\"title1\">text1</a>abc
72/// abc<a href=\"dest2\" title=\"title2\">text2</a>abc
73/// <a href=\"dest3\" title=\"title3\">[text3]: dest3 \"title3\"</a>
74/// <a href=\"dest1\" title=\"title1\">[label1]: dest1 \"title1\"</a>
75/// abc<a href=\"dest3\" title=\"title3\">text3</a>abc
76/// abc<a href=\"mailto:foo@dest4\" title=\"\">foo@dest4</a>abc
77/// abc<img src=\"dest5\" alt=\"alt5\">abc
78/// abc<a href=\"dest6\" title=\"\"><img alt=\"alt6\" src=\"src6\"></a>abc
79/// </pre>";
80/// let res = text_links2html(i);
81/// assert_eq!(res, expected);
82/// ```
83///
84/// ### Rendered text
85///
86/// This is how the rendered text looks like in the browser:
87///
88/// <pre>abc<a href="dest0" title="title0">text0</a>abc
89/// abc<a href="dest1" title="title1">text1</a>abc
90/// abc<a href="dest2" title="title2">text2</a>abc
91/// <a href="dest3" title="title3">[text3]: dest3 &quot;title3&quot;</a>
92/// <a href="dest1" title="title1">[label1]: dest1 &quot;title1&quot;</a>
93/// abc<a href="dest3" title="title3">text3</a>abc
94/// abc<a href="foo@dest4" title="">foo@dest4</a>abc
95/// abc<img alt="alt5\" src=\"dest5\">abc
96/// abc<a href="dest6" title=""><img alt="alt6" src="src6"></a>abc
97/// </pre>
98///
99/// ## reStructuredText
100/// ```
101/// use parse_hyperlinks::renderer::text_links2html;
102/// use std::borrow::Cow;
103///
104/// let i = r#"abc `text1 <label1_>`_abc
105/// abc text2_ abc
106/// abc text3__ abc
107/// abc text_label4_ abc
108/// abc text5__ abc
109/// .. _label1: dest1
110/// .. _text2: dest2
111/// .. __: dest3
112/// __ dest5
113/// "#;
114///
115/// let expected = "\
116/// <pre>abc <a href=\"dest1\" title=\"\">text1</a>abc
117/// abc <a href=\"dest2\" title=\"\">text2</a> abc
118/// abc <a href=\"dest3\" title=\"\">text3</a> abc
119/// abc text_label4_ abc
120/// abc <a href=\"dest5\" title=\"\">text5</a> abc
121/// <a href=\"dest1\" title=\"\">.. _label1: dest1</a>
122/// <a href=\"dest2\" title=\"\">.. _text2: dest2</a>
123/// <a href=\"dest3\" title=\"\">.. __: dest3</a>
124/// <a href=\"dest5\" title=\"\">__ dest5</a>
125/// </pre>\
126/// ";
127///
128/// let res = text_links2html(i);
129/// assert_eq!(res, expected);
130/// ```
131///
132/// ### Rendered text
133///
134/// This is how the rendered text looks like in the browser:
135///
136/// <pre>abc <a href="dest1" title="">text1</a>abc
137/// abc <a href="dest2" title="">text2</a> abc
138/// abc <a href="dest3" title="">text3</a> abc
139/// abc text_label4_ abc
140/// abc <a href="dest5" title="">text5</a> abc
141/// <a href="dest1" title="">.. _label1: dest1</a>
142/// <a href="dest2" title="">.. _text2: dest2</a>
143/// <a href="dest3" title="">.. __: dest3</a>
144/// <a href="dest5" title="">__ dest5</a>
145/// </pre>
146///
147/// ## Asciidoc
148///
149/// ```
150/// use parse_hyperlinks::renderer::text_links2html;
151/// use std::borrow::Cow;
152///
153/// let i = r#"abc https://dest0[text0]abc
154/// abc link:https://dest1[text1]abc
155/// abc{label2}[text2]abc
156/// abc{label3}abc
157/// :label2: https://dest2
158/// :label3: https://dest3
159/// "#;
160///
161/// let expected = "\
162/// <pre>abc <a href=\"https://dest0\" title=\"\">text0</a>abc
163/// abc <a href=\"https://dest1\" title=\"\">text1</a>abc
164/// abc<a href=\"https://dest2\" title=\"\">text2</a>abc
165/// abc<a href=\"https://dest3\" title=\"\">https://dest3</a>abc
166/// <a href=\"https://dest2\" title=\"\">:label2: https://dest2</a>
167/// <a href=\"https://dest3\" title=\"\">:label3: https://dest3</a>
168/// </pre>";
169///
170/// let res = text_links2html(i);
171/// assert_eq!(res, expected);
172/// ```
173///
174/// ### Rendered text
175///
176/// This is how the rendered text looks like in the browser:
177///
178/// <pre>abc <a href="https://dest0" title="">text0</a>abc
179/// abc <a href="https://dest1" title="">text1</a>abc
180/// abc<a href="https://dest2" title="">text2</a>abc
181/// abc<a href="https://dest3" title="">https://dest3</a>abc
182/// <a href="https://dest2" title="">:label2: https://dest2</a>
183/// <a href="https://dest3" title="">:label3: https://dest3</a>
184/// </pre>
185///
186///
187/// ## Wikitext
188///
189/// ```
190/// use parse_hyperlinks::renderer::text_links2html;
191/// use std::borrow::Cow;
192///
193/// let i = r#"abc[https://dest0 text0]abc
194/// "#;
195///
196/// let expected = "\
197/// <pre>abc<a href=\"https://dest0\" title=\"\">text0</a>abc
198/// </pre>";
199///
200/// let res = text_links2html(i);
201/// assert_eq!(res, expected);
202/// ```
203///
204/// ### Rendered text
205///
206/// This is how the rendered text looks like in the browser:
207///
208/// <pre>abc<a href="https://dest0" title="">text0</a>abc
209/// </pre>
210///
211///
212/// ## HTML
213///
214/// HTML _inline links_ are sanitized and passed through.
215///
216/// ```
217/// use parse_hyperlinks::renderer::text_links2html;
218/// use std::borrow::Cow;
219///
220/// let i = "abc<a href=\"dest1\" title=\"title1\">text1</a>abc
221/// abc<img src=\"dest5\" alt=\"alt5\">abc
222/// abc<a href=\"dest6\" title=\"\"><img alt=\"alt6\" src=\"src6\"></a>abc
223/// ";
224///
225/// let expected = "<pre>abc<a href=\"dest1\" title=\"title1\">text1</a>abc
226/// abc<img src=\"dest5\" alt=\"alt5\">abc
227/// abc<a href=\"dest6\" title=\"\"><img alt=\"alt6\" src=\"src6\"></a>abc
228/// </pre>";
229///
230/// let res = text_links2html(i);
231/// assert_eq!(res, expected);
232/// ```
233///
234/// ### Rendered text
235///
236/// This is how the rendered text looks like in the browser:
237///
238/// <pre>
239/// abc<a href="dest1" title="title1">text1</a>abc
240/// </pre>
241///
242#[inline]
243pub fn text_links2html(input: &str) -> String {
244    let mut output = Vec::new();
245    text_links2html_writer(input, &mut output).unwrap_or_default();
246    // We know this is safe, because only `str` have been written into `output`.
247    // So the following would be fine, but I want to keep this crate `unsafe`-free.
248    //    unsafe {String::from_utf8_unchecked(output)}
249    String::from_utf8(output).unwrap_or_default()
250}
251
252/// # Source code viewer with link renderer
253///
254/// Same as `text_links2html()`, but it uses `Write` for output. This function
255/// allocates much less memory and is faster because it avoids copying.
256///
257/// Usage example:
258/// ```no_run
259/// use parse_hyperlinks::renderer::text_links2html_writer;
260/// use std::io;
261/// use std::io::Read;
262/// fn main() -> Result<(), ::std::io::Error> {
263///     let mut stdin = String::new();
264///     Read::read_to_string(&mut io::stdin(), &mut stdin)?;
265///
266///     text_links2html_writer(&stdin, &mut io::stdout())?;
267///
268///     Ok(())
269/// }
270/// ```
271pub fn text_links2html_writer<'a, W>(input: &'a str, output: &mut W) -> Result<(), io::Error>
272where
273    W: Write,
274{
275    let verb_renderer = |verb: Cow<'a, str>| verb;
276
277    let link_renderer = |(_consumed, link)| match link {
278        Link::Text2Dest(text, dest, title) => format!(
279            r#"<a href="{}" title="{}">{}</a>"#,
280            encode_double_quoted_attribute(dest.as_ref()),
281            encode_double_quoted_attribute(title.as_ref()),
282            text
283        ),
284        Link::Image2Dest(text1, alt, src, text2, dest, title) => format!(
285            r#"<a href="{}" title="{}">{}<img alt="{}" src="{}">{}</a>"#,
286            encode_double_quoted_attribute(dest.as_ref()),
287            encode_double_quoted_attribute(title.as_ref()),
288            text1,
289            encode_double_quoted_attribute(alt.as_ref()),
290            encode_double_quoted_attribute(src.as_ref()),
291            text2,
292        ),
293        Link::Image(alt, src) => format!(
294            r#"<img src="{}" alt="{}">"#,
295            encode_double_quoted_attribute(src.as_ref()),
296            encode_double_quoted_attribute(alt.as_ref()),
297        ),
298        e => format!("<ERROR rendering: {:?}>", e),
299    };
300
301    render(
302        input,
303        "<pre>",
304        "</pre>",
305        verb_renderer,
306        link_renderer,
307        true,
308        output,
309    )
310}
311
312/// # Markup source code viewer
313///
314/// Markup source code viewer, that make hyperlinks
315/// clickable in your web-browser.
316///
317/// This function prints the input text “as it is”, but
318/// renders links with markup. Links are clickable.
319///
320/// ## Markdown
321/// ```
322/// use parse_hyperlinks::renderer::text_rawlinks2html;
323/// use std::borrow::Cow;
324///
325/// let i = r#"abc[text0](dest0 "title0")abc
326/// abc[text1][label1]abc
327/// abc[text2](dest2 "title2")abc
328/// [text3]: dest3 "title3"
329/// [label1]: dest1 "title1"
330/// abc[text3]abc
331/// abc<foo@dest4>abc
332/// abc![alt5](dest5)abc
333/// abc[![alt6](src6)](dest6)abc
334/// "#;
335///
336/// let expected = "\
337/// <pre>abc<a href=\"dest0\" title=\"title0\">[text0](dest0 \"title0\")</a>abc
338/// abc<a href=\"dest1\" title=\"title1\">[text1][label1]</a>abc
339/// abc<a href=\"dest2\" title=\"title2\">[text2](dest2 \"title2\")</a>abc
340/// <a href=\"dest3\" title=\"title3\">[text3]: dest3 \"title3\"</a>
341/// <a href=\"dest1\" title=\"title1\">[label1]: dest1 \"title1\"</a>
342/// abc<a href=\"dest3\" title=\"title3\">[text3]</a>abc
343/// abc<a href=\"mailto:foo@dest4\" title=\"\">&lt;foo@dest4&gt;</a>abc
344/// abc<a href=\"dest5\" title=\"alt5\">![alt5](dest5)</a>abc
345/// abc<a href=\"dest6\" title=\"\">[![alt6](src6)](dest6)</a>abc
346/// </pre>";
347///
348/// let res = text_rawlinks2html(i);
349/// assert_eq!(res, expected);
350/// ```
351///
352/// ### Rendered text
353///
354/// This is how the rendered text looks like in the browser:
355///
356/// <pre>abc<a href="dest0" title="title0">[text0](dest0 "title0")</a>abc
357/// abc<a href="dest1" title="title1">[text1][label1]</a>abc
358/// abc<a href="dest2" title="title2">[text2](dest2 "title2")</a>abc
359/// <a href="dest3" title="title3">[text3]: dest3 "title3"</a>
360/// <a href="dest1" title="title1">[label1]: dest1 "title1"</a>
361/// abc<a href="dest5" title="alt5">![alt5](dest5)</a>abc
362/// abc<a href="dest6" title="">[![alt6](src6)](dest6)</a>abc
363/// </pre>
364///
365/// ## reStructuredText
366/// ```
367/// use parse_hyperlinks::renderer::text_rawlinks2html;
368/// use std::borrow::Cow;
369///
370/// let i = r#"
371/// abc `text1 <label1_>`_abc
372/// abc text2_ abc
373/// abc text3__ abc
374/// abc text_label4_ abc
375/// abc text5__ abc
376/// .. _label1: dest1
377/// .. _text2: dest2
378/// .. __: dest3
379/// __ dest5
380/// "#;
381///
382/// let expected = "\
383/// <pre>
384/// abc <a href=\"dest1\" title=\"\">`text1 &lt;label1_&gt;`_</a>abc
385/// abc <a href=\"dest2\" title=\"\">text2_</a> abc
386/// abc <a href=\"dest3\" title=\"\">text3__</a> abc
387/// abc text_label4_ abc
388/// abc <a href=\"dest5\" title=\"\">text5__</a> abc
389/// <a href=\"dest1\" title=\"\">.. _label1: dest1</a>
390/// <a href=\"dest2\" title=\"\">.. _text2: dest2</a>
391/// <a href=\"dest3\" title=\"\">.. __: dest3</a>
392/// <a href=\"dest5\" title=\"\">__ dest5</a>
393/// </pre>";
394///
395/// let res = text_rawlinks2html(i);
396/// assert_eq!(res, expected);
397/// ```
398///
399/// ### Rendered text
400///
401/// This is how the rendered text look likes in the browser:
402///
403/// <pre>
404/// abc <a href="dest1" title="">`text1 &lt;label1_&gt;`_</a>abc
405/// abc <a href="dest2" title="">text2_</a> abc
406/// abc <a href="dest3" title="">text3__</a> abc
407/// abc text_label4_ abc
408/// abc <a href="dest5" title="">text5__</a> abc
409/// <a href="dest1" title="">.. _label1: dest1</a>
410/// <a href="dest2" title="">.. _text2: dest2</a>
411/// <a href="dest3" title="">.. __: dest3</a>
412/// <a href="dest5" title="">__ dest5</a>
413/// </pre>
414///
415/// ## Asciidoc
416///
417/// ```
418/// use parse_hyperlinks::renderer::text_rawlinks2html;
419/// use std::borrow::Cow;
420///
421/// let i = r#"abc https://dest0[text0]abc
422/// abc link:https://dest1[text1]abc
423/// abc{label2}[text2]abc
424/// abc{label3}abc
425/// :label2: https://dest2
426/// :label3: https://dest3
427/// "#;
428///
429/// let expected = "\
430/// <pre>abc <a href=\"https://dest0\" title=\"\">https://dest0[text0]</a>abc
431/// abc <a href=\"https://dest1\" title=\"\">link:https://dest1[text1]</a>abc
432/// abc<a href=\"https://dest2\" title=\"\">{label2}[text2]</a>abc
433/// abc<a href=\"https://dest3\" title=\"\">{label3}</a>abc
434/// <a href=\"https://dest2\" title=\"\">:label2: https://dest2</a>
435/// <a href=\"https://dest3\" title=\"\">:label3: https://dest3</a>
436/// </pre>";
437///
438/// let res = text_rawlinks2html(i);
439/// assert_eq!(res, expected);
440/// ```
441///
442/// ### Rendered text
443///
444/// This is how the rendered text looks like in the browser:
445///
446/// <pre>abc <a href="https://dest0" title="">https://dest0[text0]</a>abc
447/// abc <a href="https://dest1" title="">link:https://dest1[text1]</a>abc
448/// abc<a href="https://dest2" title="">{label2}[text2]</a>abc
449/// abc<a href="https://dest3" title="">{label3}</a>abc
450/// <a href="https://dest2" title="">:label2: https://dest2</a>
451/// <a href="https://dest3" title="">:label3: https://dest3</a>
452/// </pre>
453///
454///
455/// ## Wikitext
456///
457/// ```
458/// use parse_hyperlinks::renderer::text_rawlinks2html;
459/// use std::borrow::Cow;
460///
461/// let i = r#"abc[https://dest0 text0]abc
462/// "#;
463///
464/// let expected = "\
465/// <pre>abc<a href=\"https://dest0\" title=\"\">[https://dest0 text0]</a>abc
466/// </pre>";
467///
468/// let res = text_rawlinks2html(i);
469/// assert_eq!(res, expected);
470/// ```
471///
472/// ### Rendered text
473///
474/// This is how the rendered text looks like in the browser:
475///
476/// <pre>abc<a href="https://dest0" title="">[https://dest0 text0]</a>abc
477/// </pre>
478///
479/// ## HTML
480///
481/// HTML _inline links_ are sanitized and their link
482/// source code is shown as _link text_.
483///
484/// ```
485/// use parse_hyperlinks::renderer::text_rawlinks2html;
486/// use std::borrow::Cow;
487///
488/// let i = "abc<a href=\"dest1\" title=\"title1\">text1</a>abc
489/// abc<img src=\"dest5\" alt=\"alt5\">abc
490/// abc<a href=\"dest6\" title=\"\"><img alt=\"alt6\" src=\"src6\"></a>abc
491/// ";
492///
493/// let expected = "<pre>abc<a href=\"dest1\" title=\"title1\">\
494/// &lt;a href=\"dest1\" title=\"title1\"&gt;text1&lt;/a&gt;</a>abc
495/// abc<a href=\"dest5\" title=\"alt5\">\
496///    &lt;img src=\"dest5\" alt=\"alt5\"&gt;</a>abc
497/// abc<a href=\"dest6\" title=\"\">&lt;a href=\"dest6\" title=\"\"&gt;\
498///    &lt;img alt=\"alt6\" src=\"src6\"&gt;&lt;/a&gt;</a>abc
499/// </pre>";
500///
501/// let res = text_rawlinks2html(i);
502/// assert_eq!(res, expected);
503/// ```
504///
505/// ### Rendered text
506///
507/// This is how the rendered text looks like in the browser:
508///
509/// <pre>
510/// abc<a href="dest1" title="title1">&lt;a href="dest1" title="title1"&gt;text1&lt;/a&gt;</a>abc
511/// </pre>
512///
513#[inline]
514pub fn text_rawlinks2html(input: &str) -> String {
515    let mut output = Vec::new();
516    text_rawlinks2html_writer(input, &mut output).unwrap_or_default();
517    // We know this is safe, because only `str` have been written into `output`.
518    // So the following would be fine, but I want to keep this crate `unsafe`-free.
519    //    unsafe {String::from_utf8_unchecked(output)}
520    String::from_utf8(output).unwrap_or_default()
521}
522
523/// # Markup source code viewer
524///
525/// Same as `text_rawlinks2html()`, but it uses `Write` for output. This function
526/// allocates much less memory and is faster because it avoids copying.
527///
528/// Usage example:
529/// ```no_run
530/// use parse_hyperlinks::renderer::text_rawlinks2html_writer;
531/// use std::io;
532/// use std::io::Read;
533/// fn main() -> Result<(), ::std::io::Error> {
534///     let mut stdin = String::new();
535///     Read::read_to_string(&mut io::stdin(), &mut stdin)?;
536///
537///     text_rawlinks2html_writer(&stdin, &mut io::stdout())?;
538///
539///     Ok(())
540/// }
541/// ```
542pub fn text_rawlinks2html_writer<'a, W>(input: &'a str, output: &mut W) -> Result<(), io::Error>
543where
544    W: Write,
545{
546    let verb_renderer = |verb: Cow<'a, str>| verb;
547
548    let link_renderer = |(consumed, link)| match link {
549        Link::Text2Dest(_text, dest, title) => format!(
550            r#"<a href="{}" title="{}">{}</a>"#,
551            encode_double_quoted_attribute(dest.as_ref()),
552            encode_double_quoted_attribute(title.as_ref()),
553            consumed
554        ),
555        Link::Image2Dest(_text1, _alt, _src, _text2, dest, title) => format!(
556            r#"<a href="{}" title="{}">{}</a>"#,
557            encode_double_quoted_attribute(dest.as_ref()),
558            encode_double_quoted_attribute(title.as_ref()),
559            consumed
560        ),
561        Link::Image(alt, src) => format!(
562            r#"<a href="{}" title="{}">{}</a>"#,
563            encode_double_quoted_attribute(src.as_ref()),
564            encode_double_quoted_attribute(alt.as_ref()),
565            consumed
566        ),
567        e => format!("<ERROR rendering: {:?}>", e),
568    };
569
570    render(
571        input,
572        "<pre>",
573        "</pre>",
574        verb_renderer,
575        link_renderer,
576        true,
577        output,
578    )
579}
580
581/// # Hyperlink extractor
582///
583/// Text to HTML renderer that prints only links with markup as
584/// a list, one per line. Links are clickable and only their
585/// _link text_ is shown (the part enclosed with `<a>` and `</a>`).
586///
587/// ## Markdown
588/// ```
589/// use parse_hyperlinks::renderer::links2html;
590/// use std::borrow::Cow;
591///
592/// let i = r#"abc[text0](dest0 "title0")abc
593/// abc[text1][label1]abc
594/// abc[text2](dest2 "title2")abc
595/// [text3]: dest3 "title3"
596/// [label1]: dest1 "title1"
597/// abc[text3]abc
598/// abc<foo@dest4>abc
599/// abc![alt5](dest5)abc
600/// abc[![alt6](src6)](dest6)abc
601/// "#;
602///
603/// let expected = "\
604/// <a href=\"dest0\" title=\"title0\">text0</a><br>
605/// <a href=\"dest1\" title=\"title1\">text1</a><br>
606/// <a href=\"dest2\" title=\"title2\">text2</a><br>
607/// <a href=\"dest3\" title=\"title3\">text3</a><br>
608/// <a href=\"mailto:foo@dest4\" title=\"\">foo@dest4</a><br>
609/// <a href=\"dest5\">[alt5]</a><br>
610/// <a href=\"dest6\" title=\"\">[alt6]</a><br>
611/// ";
612/// let res = links2html(i);
613/// assert_eq!(res, expected);
614/// ```
615///
616/// ### Rendered text
617///
618/// This is how the rendered text looks like in the browser:
619///
620/// <a href="dest0" title="title0">text0</a><br>
621/// <a href="dest1" title="title1">text1</a><br>
622/// <a href="dest2" title="title2">text2</a><br>
623/// <a href="dest3" title="title3">text3</a><br>
624/// <a href="mailto:foo@dest4" title="">foo@dest4</a><br>
625/// <a href="dest5">[alt5]</a><br>
626/// <a href="dest6" title="">[alt6]</a><br>
627///
628///
629/// ## reStructuredText
630/// ```
631/// use parse_hyperlinks::renderer::links2html;
632/// use std::borrow::Cow;
633///
634/// let i = r#"
635/// abc `text1 <label1_>`_abc
636/// abc text2_ abc
637/// abc text3__ abc
638/// abc text_label4_ abc
639/// abc text5__ abc
640/// .. _label1: dest1
641/// .. _text2: dest2
642/// .. __: dest3
643/// __ dest5
644/// "#;
645///
646/// let expected = "\
647/// <a href=\"dest1\" title=\"\">text1</a><br>
648/// <a href=\"dest2\" title=\"\">text2</a><br>
649/// <a href=\"dest3\" title=\"\">text3</a><br>
650/// <a href=\"dest5\" title=\"\">text5</a><br>
651/// ";
652///
653/// let res = links2html(i);
654/// assert_eq!(res, expected);
655/// ```
656///
657/// ### Rendered text
658///
659/// This is how the rendered text looks like in the browser:
660///
661/// <a href="dest1" title="">text1</a><br>
662/// <a href="dest2" title="">text2</a><br>
663/// <a href="dest3" title="">text3</a><br>
664/// <a href="dest5" title="">text5</a><br>
665///
666///
667/// ## Asciidoc
668///
669/// ```
670/// use parse_hyperlinks::renderer::links2html;
671/// use std::borrow::Cow;
672///
673/// let i = r#"abc https://dest0[text0]abc
674/// abc link:https://dest1[text1]abc
675/// abc{label2}[text2]abc
676/// abc{label3}abc
677/// :label2: https://dest2
678/// :label3: https://dest3
679/// "#;
680///
681/// let expected = "\
682/// <a href=\"https://dest0\" title=\"\">text0</a><br>
683/// <a href=\"https://dest1\" title=\"\">text1</a><br>
684/// <a href=\"https://dest2\" title=\"\">text2</a><br>
685/// <a href=\"https://dest3\" title=\"\">https://dest3</a><br>
686/// ";
687///
688/// let res = links2html(i);
689/// assert_eq!(res, expected);
690/// ```
691///
692/// ### Rendered text
693///
694/// This is how the rendered text looks like in the browser:
695///
696/// <a href="https://dest0" title="">text0</a><br>
697/// <a href="https://dest1" title="">text1</a><br>
698/// <a href="https://dest2" title="">text2</a><br>
699/// <a href="https://dest3" title="">https://dest3</a><br>
700///
701///
702/// ## Wikitext
703///
704/// ```
705/// use parse_hyperlinks::renderer::links2html;
706/// use std::borrow::Cow;
707///
708/// let i = r#"abc[https://dest0 text0]abc
709/// "#;
710///
711/// let expected = "\
712/// <a href=\"https://dest0\" title=\"\">text0</a><br>
713/// ";
714///
715/// let res = links2html(i);
716/// assert_eq!(res, expected);
717/// ```
718///
719/// ### Rendered text
720///
721/// This is how the rendered text looks like in the browser:
722///
723/// <a href="https://dest0" title="">text0</a><br>
724///
725///
726/// ## HTML
727///
728/// HTML _inline links_ are sanitized and listed, one per line.
729///
730/// ```
731/// use parse_hyperlinks::renderer::links2html;
732/// use std::borrow::Cow;
733///
734/// let i = "abc<a href=\"dest1\" title=\"title1\">text1</a>abc
735/// abc<img src=\"dest5\" alt=\"alt5\">abc
736/// abc<a href=\"dest6\" title=\"\"><img alt=\"alt6\" src=\"src6\"></a>abc
737/// ";
738///
739/// let expected = "\
740/// <a href=\"dest1\" title=\"title1\">text1</a><br>
741/// <a href=\"dest5\">[alt5]</a><br>
742/// <a href=\"dest6\" title=\"\">[alt6]</a><br>
743/// ";
744///
745/// let res = links2html(i);
746/// assert_eq!(res, expected);
747/// ```
748///
749/// ### Rendered text
750///
751/// This is how the rendered text looks like in the browser:
752///
753/// <a href="dest1" title="title1">text1</a><br>
754/// <a href="dest2" title="title2">text2</a><br>
755///
756#[inline]
757pub fn links2html(input: &str) -> String {
758    let mut output = Vec::new();
759    links2html_writer(input, &mut output).unwrap_or_default();
760    // We know this is safe, because only `str` have been written into `output`.
761    // So the following would be fine, but I want to keep this crate `unsafe`-free.
762    //    unsafe {String::from_utf8_unchecked(output)}
763    String::from_utf8(output).unwrap_or_default()
764}
765
766/// # Hyperlink extractor
767///
768/// Same as `links2html()`, but it uses `Write` for output. This function
769/// allocates much less memory and is faster because it avoids copying.
770///
771/// Usage example:
772/// ```no_run
773/// use parse_hyperlinks::renderer::links2html_writer;
774/// use std::io;
775/// use std::io::Read;
776/// fn main() -> Result<(), ::std::io::Error> {
777///     let mut stdin = String::new();
778///     Read::read_to_string(&mut io::stdin(), &mut stdin)?;
779///
780///     links2html_writer(&stdin, &mut io::stdout())?;
781///
782///     Ok(())
783/// }
784/// ```
785pub fn links2html_writer<'a, S: 'a + AsRef<str>, W: Write>(
786    input: S,
787    output: &mut W,
788) -> Result<(), io::Error> {
789    let input = input.as_ref();
790
791    let verb_renderer = |_| Cow::Borrowed("");
792
793    let link_renderer = |(_consumed, link)| match link {
794        Link::Text2Dest(text, dest, title) => format!(
795            "<a href=\"{}\" title=\"{}\">{}</a><br>\n",
796            encode_double_quoted_attribute(dest.as_ref()),
797            encode_double_quoted_attribute(title.as_ref()),
798            text
799        ),
800        Link::Image2Dest(text1, alt, _src, text2, dest, title) => format!(
801            "<a href=\"{}\" title=\"{}\">{}[{}]{}</a><br>\n",
802            encode_double_quoted_attribute(dest.as_ref()),
803            encode_double_quoted_attribute(title.as_ref()),
804            text1,
805            if !alt.is_empty() { &alt } else { &dest },
806            text2,
807        ),
808        Link::Image(alt, src) => format!(
809            "<a href=\"{}\">[{}]</a><br>\n",
810            encode_double_quoted_attribute(src.as_ref()),
811            if !alt.is_empty() { &alt } else { &src },
812        ),
813        e => format!("<ERROR rendering: {:?}>", e),
814    };
815
816    render(input, "", "", verb_renderer, link_renderer, false, output)
817}
818
819#[cfg(test)]
820mod tests {
821    use super::*;
822
823    #[test]
824    fn test_text_links2html() {
825        let i = r#"abc[text1][label1]abc
826abc [text2](destination2 "title2")
827  [label3]: destination3 "title3"
828  [label1]: destination1 "title1"
829abc[label3]abc[label4]abc
830"#;
831
832        let expected = r#"<pre>abc<a href="destination1" title="title1">text1</a>abc
833abc <a href="destination2" title="title2">text2</a>
834  <a href="destination3" title="title3">[label3]: destination3 "title3"</a>
835  <a href="destination1" title="title1">[label1]: destination1 "title1"</a>
836abc<a href="destination3" title="title3">label3</a>abc[label4]abc
837</pre>"#;
838        let res = text_links2html(i);
839        //eprintln!("{}", res);
840        assert_eq!(res, expected);
841
842        //
843        let i = r#"![alt1](src1)abc
844![](src2)abc
845[![alt3](src3)](dest3)abc
846[![](src4)](dest4)abc
847"#;
848        let expected = r#"<pre><img src="src1" alt="alt1">abc
849<img src="src2" alt="">abc
850<a href="dest3" title=""><img alt="alt3" src="src3"></a>abc
851<a href="dest4" title=""><img alt="" src="src4"></a>abc
852</pre>"#;
853        let res = text_links2html(i);
854        //eprintln!("{}", res);
855        assert_eq!(res, expected);
856    }
857
858    #[test]
859    fn test_text_links2html2() {
860        let i = r#"abc
861abc
862"#;
863
864        let expected = r#"<pre>abc
865abc
866</pre>"#;
867        let res = text_links2html(i);
868        //eprintln!("{}", res);
869        assert_eq!(res, expected);
870    }
871
872    #[test]
873    fn test_text_rawlinks2html() {
874        let i = r#"abc[text1][label1]abc
875abc [text2](destination2 "title2")
876  [label3]: destination3 "title3"
877  [label1]: destination1 "title1"
878abc[label3]abc[label4]abc
879"#;
880        let expected = r#"<pre>abc<a href="destination1" title="title1">[text1][label1]</a>abc
881abc <a href="destination2" title="title2">[text2](destination2 "title2")</a>
882  <a href="destination3" title="title3">[label3]: destination3 "title3"</a>
883  <a href="destination1" title="title1">[label1]: destination1 "title1"</a>
884abc<a href="destination3" title="title3">[label3]</a>abc[label4]abc
885</pre>"#;
886        let res = text_rawlinks2html(i);
887        //eprintln!("{}", res);
888        assert_eq!(res, expected);
889
890        //
891        let i = r#"![alt1](src1)abc
892![](src2)abc
893[![alt3](src3)](dest3)abc
894[![](src4)](dest4)abc
895"#;
896        let expected = r#"<pre><a href="src1" title="alt1">![alt1](src1)</a>abc
897<a href="src2" title="">![](src2)</a>abc
898<a href="dest3" title="">[![alt3](src3)](dest3)</a>abc
899<a href="dest4" title="">[![](src4)](dest4)</a>abc
900</pre>"#;
901        let res = text_rawlinks2html(i);
902        //eprintln!("{}", res);
903        assert_eq!(res, expected);
904    }
905
906    #[test]
907    fn test_links2html() {
908        let i = r#"abc[text1][label1]abc
909abc [text2](destination2 "title2")
910  [label3]: destination3 "title3"
911  [label1]: destination1 "title1"
912abc[label3]abc[label4]abc
913"#;
914
915        let expected = r#"<a href="destination1" title="title1">text1</a><br>
916<a href="destination2" title="title2">text2</a><br>
917<a href="destination3" title="title3">label3</a><br>
918"#;
919        let res = links2html(i);
920        //eprintln!("{}", res);
921        assert_eq!(res, expected);
922
923        //
924        let i = r#"![alt1](src1)abc
925![](src2)abc
926[![alt3](src3)](dest3)abc
927[![](src4)](dest4)abc
928"#;
929
930        let expected = r#"<a href="src1">[alt1]</a><br>
931<a href="src2">[src2]</a><br>
932<a href="dest3" title="">[alt3]</a><br>
933<a href="dest4" title="">[dest4]</a><br>
934"#;
935        let res = links2html(i);
936        //eprintln!("{}", res);
937        assert_eq!(res, expected);
938    }
939}