parse_hyperlinks/parser/
parse.rs

1//! This module implements parsers to extract hyperlinks and link reference
2//! definitions from text input. The parsers search for Markdown,
3//! ReStructuredText, Asciidoc, Wikitext and HTML formatted links.
4#![allow(dead_code)]
5#![allow(clippy::type_complexity)]
6
7use crate::parser::asciidoc::adoc_label2dest_link;
8use crate::parser::asciidoc::adoc_text2dest_link;
9use crate::parser::asciidoc::adoc_text2label_link;
10use crate::parser::html::html_text2dest_link;
11use crate::parser::html_img::html_img2dest_link;
12use crate::parser::html_img::html_img_link;
13use crate::parser::markdown::md_label2dest_link;
14use crate::parser::markdown::md_text2dest_link;
15use crate::parser::markdown::md_text2label_link;
16use crate::parser::markdown_img::md_img2dest_link;
17use crate::parser::markdown_img::md_img_link;
18use crate::parser::restructured_text::rst_label2dest_link;
19use crate::parser::restructured_text::rst_label2label_link;
20use crate::parser::restructured_text::rst_text2dest_link;
21use crate::parser::restructured_text::rst_text2label_link;
22use crate::parser::restructured_text::rst_text_label2dest_link;
23use crate::parser::wikitext::wikitext_text2dest_link;
24use crate::parser::Link;
25use nom::branch::alt;
26use nom::bytes::complete::take_till;
27use nom::character::complete::anychar;
28use std::borrow::Cow;
29
30/// Link max label. This limits the damage of a forgotten closing brackets.
31/// [CommonMark Spec](https://spec.commonmark.org/0.30/#link-label)
32pub const LABEL_LEN_MAX: usize = 999;
33
34/// Consumes the input until it finds a Markdown, RestructuredText, Asciidoc or
35/// HTML formatted _inline link_ (`Text2Dest`) or _link reference definition_
36/// (`Label2Dest`).
37///
38/// Returns `Ok((remaining_input, (link_text_or_label, link_destination,
39/// link_title)))`. The parser recognizes only stand alone _inline links_ and
40/// _link reference definitions_, but no _reference links_.
41///
42/// # Limitations:
43/// Link reference labels are never resolved into link text. This limitation only
44/// concerns this parser. Others are not affected.
45///
46/// Very often this limitation has no effect at all. This is the case, when the
47/// _link text_ and the _link label_ are identical:
48///
49/// ```md
50/// abc [link text/label] abc
51///
52/// [link text/label]: /url "title"
53/// ```
54///
55/// But in general, the _link text_ and the _link label_ can be different:
56///
57/// ```md
58/// abc [link text][link label] abc
59///
60/// [link label]: /url "title"
61/// ```
62///
63/// When a link reference definition is found, the parser outputs it's link label
64/// instead of the link text, which is strictly speaking only correct when both
65/// are identical. Note, the same applies to RestructuredText's link reference
66/// definitions too.
67///
68/// Another limitation is that ReStructuredText's anonymous links are not supported.
69///
70///
71/// # Basic usage
72///
73/// ```
74/// use parse_hyperlinks::parser::parse::take_text2dest_label2dest;
75/// use std::borrow::Cow;
76///
77/// let i = r#"[a]: b 'c'
78///            .. _d: e
79///            ---[f](g 'h')---`i <j>`_---
80///            ---<a href="l" title="m">k</a>"#;
81///
82/// let (i, r) = take_text2dest_label2dest(i).unwrap();
83/// assert_eq!(r, (Cow::from("a"), Cow::from("b"), Cow::from("c")));
84/// let (i, r) = take_text2dest_label2dest(i).unwrap();
85/// assert_eq!(r, (Cow::from("d"), Cow::from("e"), Cow::from("")));
86/// let (i, r) = take_text2dest_label2dest(i).unwrap();
87/// assert_eq!(r, (Cow::from("f"), Cow::from("g"), Cow::from("h")));
88/// let (i, r) = take_text2dest_label2dest(i).unwrap();
89/// assert_eq!(r, (Cow::from("i"), Cow::from("j"), Cow::from("")));
90/// let (i, r) = take_text2dest_label2dest(i).unwrap();
91/// assert_eq!(r, (Cow::from("k"), Cow::from("l"), Cow::from("m")));
92/// ```
93/// The parser might silently consume some additional bytes after the actual finding: This happens,
94/// when directly after a finding a `md_link_ref` or `rst_link_ref` appears. These must be ignored,
95/// as they are only allowed at the beginning of a line. The skip has to happen at this moment, as
96/// the next parser does not know if the first byte it gets, is it at the beginning of a line or
97/// not.
98///
99/// Technically, this parser is a wrapper around `take_link()`, that erases the
100/// link type information and ignores all _reference links_. In case the input
101/// text contains _link reference definitions_, this function is be faster than
102/// the `parse_hyperlinks::iterator::Hyperlink` iterator.
103///
104/// Note: This function is depreciated and will be removed in some later release.
105/// Use `take_link()` instead.
106pub fn take_text2dest_label2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
107    let mut j = i;
108    loop {
109        match take_link(j) {
110            Ok((j, (_, Link::Text2Dest(lte, ld, lti)))) => return Ok((j, (lte, ld, lti))),
111            Ok((j, (_, Link::TextLabel2Dest(lte, ld, lti)))) => return Ok((j, (lte, ld, lti))),
112            Ok((j, (_, Link::Label2Dest(ll, ld, lti)))) => return Ok((j, (ll, ld, lti))),
113            // We ignore `Link::Ref()` and `Link::RefAlias`. Instead we continue parsing.
114            Ok((k, _)) => {
115                j = k;
116                continue;
117            }
118            Err(e) => return Err(e),
119        };
120    }
121}
122
123/// Consumes the input until it finds a Markdown, RestructuredText, Asciidoc or
124/// HTML formatted _inline link_ (`Text2Dest`), _reference link_ (`Text2Label`),
125/// _link reference definition_ (`Label2Dest`) or _reference alias_ (`Label2Label`).
126///
127/// The parser consumes the finding and returns
128/// `Ok((remaining_input, (skipped_input, Link)))` or some error.
129///
130/// # Markdown
131///
132/// ```
133/// use parse_hyperlinks::parser::Link;
134/// use parse_hyperlinks::parser::parse::take_link;
135/// use std::borrow::Cow;
136///
137/// let i = r#"abc[text1][label1]abc
138/// abc[text2](destination2 "title2")
139/// [label1]: destination1 'title1'
140/// "#;
141///
142/// let (i, r) = take_link(i).unwrap();
143/// assert_eq!(r.0, "abc");
144/// assert_eq!(r.1, Link::Text2Label(Cow::from("text1"), Cow::from("label1")));
145/// let (i, r) = take_link(i).unwrap();
146/// assert_eq!(r.0, "abc\nabc");
147/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text2"), Cow::from("destination2"), Cow::from("title2")));
148/// let (i, r) = take_link(i).unwrap();
149/// assert_eq!(r.0, "\n");
150/// assert_eq!(r.1, Link::Label2Dest(Cow::from("label1"), Cow::from("destination1"), Cow::from("title1")));
151/// ```
152/// # reStructuredText
153///
154/// ```
155/// use parse_hyperlinks::parser::Link;
156/// use parse_hyperlinks::parser::parse::take_link;
157/// use std::borrow::Cow;
158///
159/// let i = r#"abc
160/// abc `text0 <destination0>`_abc
161/// abc `text1 <destination1>`__abc
162/// abc `text2 <label2_>`_abc
163/// abc text3__ abc
164/// .. _label1: destination1
165/// .. __: destination3
166/// __ destination4
167/// "#;
168///
169/// let (i, r) = take_link(i).unwrap();
170/// assert_eq!(r.0, "abc\nabc ");
171/// assert_eq!(r.1, Link::TextLabel2Dest(Cow::from("text0"), Cow::from("destination0"), Cow::from("")));
172/// let (i, r) = take_link(i).unwrap();
173/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text1"), Cow::from("destination1"), Cow::from("")));
174/// let (i, r) = take_link(i).unwrap();
175/// assert_eq!(r.1, Link::Text2Label(Cow::from("text2"), Cow::from("label2")));
176/// let (i, r) = take_link(i).unwrap();
177/// assert_eq!(r.1, Link::Text2Label(Cow::from("text3"), Cow::from("_")));
178/// let (i, r) = take_link(i).unwrap();
179/// assert_eq!(r.1, Link::Label2Dest(Cow::from("label1"), Cow::from("destination1"), Cow::from("")));
180/// let (i, r) = take_link(i).unwrap();
181/// assert_eq!(r.1, Link::Label2Dest(Cow::from("_"), Cow::from("destination3"), Cow::from("")));
182/// let (i, r) = take_link(i).unwrap();
183/// assert_eq!(r.1, Link::Label2Dest(Cow::from("_"), Cow::from("destination4"), Cow::from("")));
184/// ```
185/// # Asciidoc
186///
187/// ```
188/// use parse_hyperlinks::parser::Link;
189/// use parse_hyperlinks::parser::parse::take_link;
190/// use std::borrow::Cow;
191///
192/// let i = r#"abc
193/// abc https://destination0[text0]abc
194/// abc link:https://destination1[text1]abc
195/// abc{label2}[text2]abc
196/// abc{label3}abc
197/// :label4: https://destination4
198/// "#;
199///
200/// let (i, r) = take_link(i).unwrap();
201/// assert_eq!(r.0, "abc\nabc ");
202/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text0"), Cow::from("https://destination0"), Cow::from("")));
203/// let (i, r) = take_link(i).unwrap();
204/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text1"), Cow::from("https://destination1"), Cow::from("")));
205/// let (i, r) = take_link(i).unwrap();
206/// assert_eq!(r.1, Link::Text2Label(Cow::from("text2"), Cow::from("label2")));
207/// let (i, r) = take_link(i).unwrap();
208/// assert_eq!(r.1, Link::Text2Label(Cow::from(""), Cow::from("label3")));
209/// let (i, r) = take_link(i).unwrap();
210/// assert_eq!(r.1, Link::Label2Dest(Cow::from("label4"), Cow::from("https://destination4"), Cow::from("")));
211/// ```
212///
213/// # HTML
214///
215/// ```
216/// use parse_hyperlinks::parser::Link;
217/// use parse_hyperlinks::parser::parse::take_link;
218/// use std::borrow::Cow;
219///
220/// let i = r#"abc<a href="destination1" title="title1">text1</a>abc
221/// abc<a href="destination2" title="title2">text2</a>abc
222/// "#;
223///
224/// let (i, r) = take_link(i).unwrap();
225/// assert_eq!(r.0, "abc");
226/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text1"), Cow::from("destination1"), Cow::from("title1")));
227/// let (i, r) = take_link(i).unwrap();
228/// assert_eq!(r.0, "abc\nabc");
229/// assert_eq!(r.1, Link::Text2Dest(Cow::from("text2"), Cow::from("destination2"), Cow::from("title2")));
230/// ```
231pub fn take_link(i: &str) -> nom::IResult<&str, (&str, Link)> {
232    let mut j = i;
233    let mut skip_count = 0;
234    let mut input_start = true;
235    let mut line_start;
236    let mut whitespace;
237    let res = loop {
238        // Are we on a new line character? consume it.
239        line_start = false;
240        // Does never fail.
241        let (k, count) = nom::multi::many0_count(nom::character::complete::newline)(j)?;
242        debug_assert_eq!(j.len() - k.len(), count);
243        if count > 0 {
244            skip_count += j.len() - k.len();
245            j = k;
246            line_start = true;
247        };
248
249        // Are we at the beginning of a line?
250        if line_start || input_start {
251            if let Ok((k, r)) = alt((
252                // Now we search for `label2*`.
253                // For both parser is the indent meaningful. We mustn't consume them.
254                rst_label2label_link,
255                rst_label2dest_link,
256            ))(j)
257            {
258                break (k, r);
259            };
260        };
261
262        // Are we on a whitespace? Now consume them.
263        whitespace = false;
264        if let (k, Some(_)) = nom::combinator::opt(nom::character::complete::space1)(j)? {
265            skip_count += j.len() - k.len();
266            j = k;
267            whitespace = true;
268        }
269
270        // Are we at the beginning of a line?
271        if line_start || input_start {
272            if let Ok((k, r)) = alt((
273                // Now we search for `label2*`.
274                // These parsers do not care about the indent, as long it is
275                // only whitespace.
276                md_label2dest_link,
277                adoc_label2dest_link,
278            ))(j)
279            {
280                break (k, r);
281            };
282        };
283        // Start searching for links.
284
285        // Regular `text` links can start everywhere.
286        if let Ok((k, r)) = alt((
287            // Start with `text2dest`.
288            md_img_link,
289            md_img2dest_link,
290            md_text2dest_link,
291            // This should be first, because it is very specific.
292            wikitext_text2dest_link,
293            // `rst_text2dest` must be always placed before `rst_text2label`.
294            rst_text2dest_link,
295            rst_text_label2dest_link,
296            adoc_text2label_link,
297            html_img_link,
298            html_img2dest_link,
299            html_text2dest_link,
300        ))(j)
301        {
302            break (k, r);
303        };
304
305        if whitespace || line_start || input_start {
306            // There must be at least one more byte. If it is one of `([<'"`, skip it.
307            let k = if let (k, Some(_)) =
308                nom::combinator::opt(nom::character::complete::one_of("([<'\""))(j)?
309            {
310                // Skip that char.
311                k
312            } else {
313                // Change nothing.
314                j
315            };
316
317            // `rst_text2label` must be always placed after `rst_text2dest`.
318            // `md_text2label` must be always placed after `adoc_text2label` and `adoc_text2dest`,
319            // because the former consumes `[*]`.
320            if let Ok((l, r)) = alt((rst_text2label_link, adoc_text2dest_link))(k) {
321                // If ever we have skipped a char, remember it now.
322                skip_count += j.len() - k.len();
323                break (l, r);
324            };
325        };
326
327        // This parser is so unspecific, that it must be the last.
328        if let Ok((k, r)) = md_text2label_link(j) {
329            break (k, r);
330        };
331
332        // This makes sure that we advance.
333        let (k, _) = anychar(j)?;
334        skip_count += j.len() - k.len();
335        j = k;
336
337        // This might not consume bytes and never fails.
338        let (k, _) = take_till(|c|
339            // After this, we should check for: `md_label2dest`, `rst_label2dest`, `rst_text2label`, `adoc_text2dest`.
340            c == '\n'
341            // After this, possible start for `adoc_text2dest` or `rst_text2label`:
342            || c == ' ' || c == '\t'
343            // These are candidates for `rst_text2label`, `rst_text_label2dest` `rst_text2dest`:
344            || c == '`'
345            // These could be the start of all `md_img` link types.
346            || c == '!'
347            // These could be the start of all `md_*` link types.
348            || c == '['
349            // These could be the start of the `adoc_text2label` link type.
350            || c == '{'
351            // And this could be an HTML hyperlink:
352            || c == '<')(j)?;
353
354        skip_count += j.len() - k.len();
355        j = k;
356        input_start = false;
357    };
358
359    // Before we return `res`, we need to check again for `md_link_ref` and
360    // `rst_link_ref` and consume them silently, without returning their result.
361    // These are only allowed at the beginning of a line and we know here, that
362    // we are not. We have to act now, because the next parser can not tell if
363    // its first byte is at the beginning of the line, because it does not know
364    // if it was called for the first time ore not. By consuming more now, we
365    // make sure that no `md_link_ref` and `rst_link_ref` is mistakenly
366    // recognized in the middle of a line.
367    // It is sufficient to do this check once, because both parser guarantee to
368    // consume the whole line in case of success.
369    let (mut l, link) = res;
370    match link {
371        Link::Label2Dest(_, _, _) | Link::Label2Label(_, _) => {}
372        _ => {
373            // Just consume, the result does not matter.
374            let (m, _) = nom::combinator::opt(alt((rst_label2dest_link, md_label2dest_link)))(l)?;
375            l = m;
376        }
377    };
378
379    let skipped_input = &i[0..skip_count];
380
381    Ok((l, (skipped_input, link)))
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387
388    #[test]
389    fn test_take_link() {
390        let expected = nom::Err::Error(nom::error::Error::new("", nom::error::ErrorKind::Eof));
391        let err = take_link("").unwrap_err();
392        assert_eq!(err, expected);
393
394        let i = r#"[md label1]: md_destination1 "md title1"
395abc [md text2](md_destination2 "md title2")[md text3]: abc[md text4]: abc
396   [md label5]: md_destination5 "md title5"
397abc `rst text1 <rst_destination1>`__abc
398abc `rst text2 <rst_label2_>`_ .. _norst: no .. _norst: no
399.. _rst label3: rst_destination3
400  .. _rst label4: rst_d
401     estination4
402__ rst_label5_
403abc `rst text_label6 <rst_destination6>`_abc
404<a href="html_destination1"
405   title="html title1">html text1</a>
406abc https://adoc_destination1[adoc text1] abc
407abc {adoc-label2}abc {adoc-label3}[adoc text3]abc
408 :adoc-label4: https://adoc_destination4
409abc{adoc-label5}abc https://adoc_destination6 abc
410abc[https://wikitext.link Wikitext Testlink]abc
411"#;
412
413        let expected = Link::Label2Dest(
414            Cow::from("md label1"),
415            Cow::from("md_destination1"),
416            Cow::from("md title1"),
417        );
418        let (i, (_, res)) = take_link(i).unwrap();
419        assert_eq!(res, expected);
420
421        let expected = Link::Text2Dest(
422            Cow::from("md text2"),
423            Cow::from("md_destination2"),
424            Cow::from("md title2"),
425        );
426        let (i, (skipped, res)) = take_link(i).unwrap();
427        assert_eq!(skipped, "\nabc ");
428        assert_eq!(res, expected);
429
430        let expected = Link::Text2Label(Cow::from("md text3"), Cow::from("md text3"));
431        let (i, (_, res)) = take_link(i).unwrap();
432        assert_eq!(res, expected);
433
434        let expected = Link::Text2Label(Cow::from("md text4"), Cow::from("md text4"));
435        let (i, (_, res)) = take_link(i).unwrap();
436        assert_eq!(res, expected);
437
438        let expected = Link::Label2Dest(
439            Cow::from("md label5"),
440            Cow::from("md_destination5"),
441            Cow::from("md title5"),
442        );
443        let (i, (_, res)) = take_link(i).unwrap();
444        assert_eq!(res, expected);
445
446        let expected = Link::Text2Dest(
447            Cow::from("rst text1"),
448            Cow::from("rst_destination1"),
449            Cow::from(""),
450        );
451        let (i, (_, res)) = take_link(i).unwrap();
452        assert_eq!(res, expected);
453
454        let expected = Link::Text2Label(Cow::from("rst text2"), Cow::from("rst_label2"));
455        let (i, (_, res)) = take_link(i).unwrap();
456        assert_eq!(res, expected);
457
458        let expected = Link::Label2Dest(
459            Cow::from("rst label3"),
460            Cow::from("rst_destination3"),
461            Cow::from(""),
462        );
463        let (i, (_, res)) = take_link(i).unwrap();
464        assert_eq!(res, expected);
465
466        let expected = Link::Label2Dest(
467            Cow::from("rst label4"),
468            Cow::from("rst_destination4"),
469            Cow::from(""),
470        );
471        let (i, (_, res)) = take_link(i).unwrap();
472        assert_eq!(res, expected);
473
474        let expected = Link::Label2Label(Cow::from("_"), Cow::from("rst_label5"));
475        let (i, (_, res)) = take_link(i).unwrap();
476        assert_eq!(res, expected);
477
478        let expected = Link::TextLabel2Dest(
479            Cow::from("rst text_label6"),
480            Cow::from("rst_destination6"),
481            Cow::from(""),
482        );
483        let (i, (_, res)) = take_link(i).unwrap();
484        assert_eq!(res, expected);
485
486        let expected = Link::Text2Dest(
487            Cow::from("html text1"),
488            Cow::from("html_destination1"),
489            Cow::from("html title1"),
490        );
491        let (i, (_, res)) = take_link(i).unwrap();
492        assert_eq!(res, expected);
493
494        let expected = Link::Text2Dest(
495            Cow::from("adoc text1"),
496            Cow::from("https://adoc_destination1"),
497            Cow::from(""),
498        );
499        let (i, (_, res)) = take_link(i).unwrap();
500        assert_eq!(res, expected);
501
502        let expected = Link::Text2Label(Cow::from(""), Cow::from("adoc-label2"));
503        let (i, (_, res)) = take_link(i).unwrap();
504        assert_eq!(res, expected);
505
506        let expected = Link::Text2Label(Cow::from("adoc text3"), Cow::from("adoc-label3"));
507        let (i, (_, res)) = take_link(i).unwrap();
508        assert_eq!(res, expected);
509
510        let expected = Link::Label2Dest(
511            Cow::from("adoc-label4"),
512            Cow::from("https://adoc_destination4"),
513            Cow::from(""),
514        );
515        let (i, (_, res)) = take_link(i).unwrap();
516        assert_eq!(res, expected);
517
518        let expected = Link::Text2Label(Cow::from(""), Cow::from("adoc-label5"));
519        let (i, (skipped, res)) = take_link(i).unwrap();
520        assert_eq!(res, expected);
521        assert_eq!(skipped, "\nabc");
522
523        let expected = Link::Text2Dest(
524            Cow::from("https://adoc_destination6"),
525            Cow::from("https://adoc_destination6"),
526            Cow::from(""),
527        );
528        let (i, (skipped, res)) = take_link(i).unwrap();
529        assert_eq!(res, expected);
530        assert_eq!(skipped, "abc ");
531
532        let expected = Link::Text2Dest(
533            Cow::from("Wikitext Testlink"),
534            Cow::from("https://wikitext.link"),
535            Cow::from(""),
536        );
537        let (_i, (skipped, res)) = take_link(i).unwrap();
538        assert_eq!(res, expected);
539        assert_eq!(skipped, " abc\nabc");
540    }
541
542    #[test]
543    fn test_take_link2() {
544        // New input:
545        // Do we find the same at the input start also?
546        let i = ".. _`My: home page`: http://getreu.net\nabc";
547        let expected = Link::Label2Dest(
548            Cow::from("My: home page"),
549            Cow::from("http://getreu.net"),
550            Cow::from(""),
551        );
552        let (i, (_, res)) = take_link(i).unwrap();
553        assert_eq!(res, expected);
554        assert_eq!(i, "\nabc");
555
556        let i = "https://adoc_link_destination[adoc link text]abc";
557        let expected = Link::Text2Dest(
558            Cow::from("adoc link text"),
559            Cow::from("https://adoc_link_destination"),
560            Cow::from(""),
561        );
562        let (i, (_, res)) = take_link(i).unwrap();
563        assert_eq!(res, expected);
564        assert_eq!(i, "abc");
565    }
566
567    #[test]
568    fn test_take_link3() {
569        let i = r#"   [md label3]: md_destination3 "md title3"
570        [md label1]: md_destination1 "md title1"
571        [md label2]: md_destination2 "md title2"
572        abc[md text_label3]abc[md text_label4]
573        "#;
574
575        let expected = Link::Label2Dest(
576            Cow::from("md label3"),
577            Cow::from("md_destination3"),
578            Cow::from("md title3"),
579        );
580        let (i, (_, res)) = take_link(i).unwrap();
581        assert_eq!(res, expected);
582
583        let expected = Link::Label2Dest(
584            Cow::from("md label1"),
585            Cow::from("md_destination1"),
586            Cow::from("md title1"),
587        );
588        let (i, (_, res)) = take_link(i).unwrap();
589        assert_eq!(res, expected);
590
591        let expected = Link::Label2Dest(
592            Cow::from("md label2"),
593            Cow::from("md_destination2"),
594            Cow::from("md title2"),
595        );
596        let (i, (_, res)) = take_link(i).unwrap();
597        assert_eq!(res, expected);
598
599        let expected = Link::Text2Label(Cow::from("md text_label3"), Cow::from("md text_label3"));
600        let (i, (_, res)) = take_link(i).unwrap();
601        assert_eq!(res, expected);
602
603        let expected = Link::Text2Label(Cow::from("md text_label4"), Cow::from("md text_label4"));
604        let (_i, (_, res)) = take_link(i).unwrap();
605        assert_eq!(res, expected);
606    }
607
608    #[test]
609    fn test_take_link4() {
610        let i = r#"
611.. _label4: label3_
612label2__
613__ label5
614"#;
615
616        let expected = Link::Label2Label(Cow::from("label4"), Cow::from("label3"));
617        let (i, (_, res)) = take_link(i).unwrap();
618        assert_eq!(res, expected);
619
620        let expected = Link::Text2Label(Cow::from("label2"), Cow::from("_"));
621        let (i, (_, res)) = take_link(i).unwrap();
622        assert_eq!(res, expected);
623
624        let expected = Link::Label2Dest(Cow::from("_"), Cow::from("label5"), Cow::from(""));
625        let (_i, (_, res)) = take_link(i).unwrap();
626        assert_eq!(res, expected);
627    }
628
629    #[test]
630    fn test_take_link5() {
631        let i = r#"[http://getreu.net](<http://blog.getreu.net>)abc"
632[http://getreu.net](<http://blog.getreu.net>)def"
633ghi[http://getreu.net](<http://blog.getreu.net>)jkl"#;
634
635        let expected = Link::Text2Dest(
636            Cow::from("http://getreu.net"),
637            Cow::from("http://blog.getreu.net"),
638            Cow::from(""),
639        );
640        let (i, (_, res)) = take_link(i).unwrap();
641        assert_eq!(res, expected);
642
643        let (_i, (_, res)) = take_link(i).unwrap();
644        assert_eq!(res, expected);
645
646        let (_i, (_, res)) = take_link(i).unwrap();
647        assert_eq!(res, expected);
648    }
649
650    #[test]
651    fn test_take_link6() {
652        let i = r#"abc<http://getreu%20Ü.net>def
653"#;
654
655        let expected = Link::Text2Dest(
656            Cow::from("http://getreu Ü.net"),
657            Cow::from("http://getreu Ü.net"),
658            Cow::from(""),
659        );
660        let (_i, (_, res)) = take_link(i).unwrap();
661        assert_eq!(res, expected);
662    }
663
664    #[test]
665    fn test_take_link7() {
666        let i = "abc[def![alt](img.png)ghi](doc.md \"title\")abc\
667            abc![alt2](dest2)abc";
668
669        let expected = Link::Image2Dest(
670            Cow::from("def"),
671            Cow::from("alt"),
672            Cow::from("img.png"),
673            Cow::from("ghi"),
674            Cow::from("doc.md"),
675            Cow::from("title"),
676        );
677        let (i, (_, res)) = take_link(i).unwrap();
678        assert_eq!(res, expected);
679
680        let expected = Link::Image(Cow::from("alt2"), Cow::from("dest2"));
681        let (i, (skipped, res)) = take_link(i).unwrap();
682        assert_eq!(res, expected);
683        assert_eq!(skipped, "abcabc");
684        assert_eq!(i, "abc");
685    }
686
687    #[test]
688    fn test_take_link8() {
689        let i = "[into_bytes](https://doc.rust-lang.org/)";
690
691        let expected = Link::Text2Dest(
692            Cow::from("into_bytes"),
693            Cow::from("https://doc.rust-lang.org/"),
694            Cow::from(""),
695        );
696        let (i, (skipped, res)) = take_link(i).unwrap();
697        assert_eq!(i, "");
698        assert_eq!(skipped, "");
699        assert_eq!(res, expected);        
700        
701        //
702        let i = "[into\\_bytes](https://doc.rust-lang.org/)";
703
704        let expected = Link::Text2Dest(
705            Cow::from("into_bytes"),
706            Cow::from("https://doc.rust-lang.org/"),
707            Cow::from(""),
708        );
709        let (i, (skipped, res)) = take_link(i).unwrap();
710        assert_eq!(i, "");
711        assert_eq!(skipped, "");
712        assert_eq!(res, expected);
713    }
714}