1#![allow(dead_code)]
3#![allow(clippy::type_complexity)]
4
5use crate::parser::Link;
6use crate::parser::parse::LABEL_LEN_MAX;
7use crate::parser::percent_decode;
8use crate::take_until_unbalanced;
9use nom::branch::alt;
10use nom::bytes::complete::tag;
11use nom::character::complete::multispace1;
12use nom::{Parser, combinator::*};
13use std::borrow::Cow;
14
15const ESCAPABLE: &str = r###"!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"###;
18
19pub fn md_text2dest_link(i: &'_ str) -> nom::IResult<&'_ str, Link<'_>> {
22 let (i, (te, de, ti)) = md_text2dest(i)?;
23 Ok((i, Link::Text2Dest(te, de, ti)))
24}
25
26pub fn md_text2dest(
49 i: &'_ str,
50) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>, Cow<'_, str>)> {
51 alt((
52 nom::sequence::delimited(
54 tag("<"),
55 map_parser(
56 nom::bytes::complete::take_till1(|c: char| {
57 c.is_ascii_whitespace() || c == '>' || c == '<'
58 }),
59 alt((md_absolute_uri, md_email_address)),
60 ),
61 tag(">"),
62 ),
63 map(
65 (md_link_text, md_link_destination_enclosed),
66 |(a, (b, c))| (a, b, c),
67 ),
68 ))
69 .parse(i)
70}
71
72pub fn md_label2dest_link(i: &'_ str) -> nom::IResult<&'_ str, Link<'_>> {
75 let (i, (l, d, t)) = md_label2dest(i)?;
76 Ok((i, Link::Label2Dest(l, d, t)))
77}
78
79pub fn md_label2dest(
121 i: &'_ str,
122) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>, Cow<'_, str>)> {
123 let (i, _) = nom::bytes::complete::take_while_m_n(0, 3, |c| c == ' ')(i)?;
125 let (i, link_text) = md_link_label(i)?;
127 let (i, _) = nom::character::complete::char(':')(i)?;
128 let (i, _) = verify(nom::character::complete::multispace1, |s: &str| {
130 !s.contains("\n\n")
131 })
132 .parse(i)?;
133 let (i, link_destination) = md_link_destination(i)?;
135 let (i, link_title) = alt((
137 md_link_title,
139 nom::combinator::success(Cow::from("")),
140 ))
141 .parse(i)?;
142
143 let (i, _) = nom::character::complete::space0(i)?;
145
146 if !i.is_empty() {
148 let _ = nom::character::complete::newline(i)?;
149 }
150
151 Ok((i, (link_text, link_destination, link_title)))
152}
153
154pub fn md_text2label_link(i: &'_ str) -> nom::IResult<&'_ str, Link<'_>> {
157 let (i, (t, l)) = md_text2label(i)?;
158 Ok((i, Link::Text2Label(t, l)))
159}
160
161pub fn md_text2label(i: &'_ str) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>)> {
199 let (i, (link_text, link_label)) = alt((
200 nom::sequence::pair(md_link_text, md_link_label),
201 nom::combinator::map(nom::sequence::terminated(md_link_text, tag("[]")), |s| {
202 (s.clone(), s)
203 }),
204 nom::combinator::map(md_link_text, |s| (s.clone(), s)),
205 ))
206 .parse(i)?;
207
208 if !i.is_empty() {
210 let _ = nom::character::complete::none_of("[(")(i)?;
211 }
212
213 Ok((i, (link_text, link_label)))
214}
215
216pub(crate) fn md_link_text(i: &'_ str) -> nom::IResult<&'_ str, Cow<'_, str>> {
224 nom::combinator::map_parser(
225 nom::sequence::delimited(tag("["), take_until_unbalanced('[', ']'), tag("]")),
226 md_escaped_str_transform,
227 )
228 .parse(i)
229}
230
231fn md_link_label(i: &'_ str) -> nom::IResult<&'_ str, Cow<'_, str>> {
240 nom::combinator::map_parser(
241 nom::combinator::verify(
242 nom::sequence::delimited(
243 tag("["),
244 nom::bytes::complete::escaped(
245 nom::character::complete::none_of("\\[]"),
246 '\\',
247 nom::character::complete::one_of(ESCAPABLE),
248 ),
249 tag("]"),
250 ),
251 |l: &str| l.len() <= LABEL_LEN_MAX,
252 ),
253 md_escaped_str_transform,
254 )
255 .parse(i)
256}
257
258pub(crate) fn md_link_destination(i: &'_ str) -> nom::IResult<&'_ str, Cow<'_, str>> {
261 nom::combinator::map_parser(md_parse_link_destination, md_escaped_str_transform).parse(i)
262}
263
264fn md_parse_link_destination(i: &str) -> nom::IResult<&str, &str> {
279 alt((
280 nom::sequence::delimited(
281 tag("<"),
282 nom::bytes::complete::escaped(
283 nom::character::complete::none_of(r#"\<>"#),
284 '\\',
285 nom::character::complete::one_of(ESCAPABLE),
286 ),
287 tag(">"),
288 ),
289 map(nom::bytes::complete::tag("<>"), |_| ""),
290 alt((
291 nom::bytes::complete::is_not(" \t\r\n"),
292 nom::combinator::success(""),
293 )),
294 ))
295 .parse(i)
296}
297
298pub(crate) fn md_link_destination_enclosed(
300 i: &'_ str,
301) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>)> {
302 map_parser(
303 nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")")),
304 (
305 md_link_destination,
306 alt((
307 md_link_title,
309 nom::combinator::success(Cow::from("")),
310 )),
311 ),
312 )
313 .parse(i)
314}
315
316fn md_link_title(i: &'_ str) -> nom::IResult<&'_ str, Cow<'_, str>> {
319 nom::combinator::map_parser(md_parse_link_title, md_escaped_str_transform).parse(i)
320}
321
322fn md_parse_link_title(i: &str) -> nom::IResult<&str, &str> {
341 nom::sequence::preceded(
342 verify(multispace1, |s: &str| !s.contains("\n\n")),
343 verify(
344 alt((
345 nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")")),
346 nom::sequence::delimited(
347 tag("'"),
348 nom::bytes::complete::escaped(
349 nom::character::complete::none_of(r#"\'"#),
350 '\\',
351 nom::character::complete::one_of(ESCAPABLE),
352 ),
353 tag("'"),
354 ),
355 nom::sequence::delimited(
356 tag("\""),
357 nom::bytes::complete::escaped(
358 nom::character::complete::none_of(r#"\""#),
359 '\\',
360 nom::character::complete::one_of(ESCAPABLE),
361 ),
362 tag("\""),
363 ),
364 )),
365 |s: &str| !s.contains("\n\n"),
366 ),
367 )
368 .parse(i)
369}
370
371fn md_escaped_str_transform(i: &'_ str) -> nom::IResult<&'_ str, Cow<'_, str>> {
373 nom::combinator::map(
374 nom::bytes::complete::escaped_transform(
375 nom::bytes::complete::is_not("\\"),
376 '\\',
377 nom::character::complete::one_of(ESCAPABLE),
378 ),
379 |s| if s == i { Cow::from(i) } else { Cow::from(s) },
380 )
381 .parse(i)
382}
383
384fn md_absolute_uri(
402 i: &'_ str,
403) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>, Cow<'_, str>)> {
404 let j = i;
405 map(
406 all_consuming(nom::sequence::separated_pair(
407 verify(
409 nom::bytes::complete::take_till1(|c: char| {
410 !(c.is_ascii_alphanumeric() || "+.-".contains(c))
411 }),
412 |s: &str| s.len() >= 2 && s.len() <= 32,
413 ),
414 tag(":"),
415 map_parser(
417 nom::bytes::complete::take_till1(|c: char| {
418 c.is_ascii_control() || c.is_ascii_whitespace() || "<>".contains(c)
419 }),
420 percent_decode,
421 ),
422 )),
423 |(scheme, domain)| {
424 let uri = if matches!(domain, Cow::Borrowed(..)) {
425 Cow::Borrowed(j)
426 } else {
427 Cow::Owned(format!("{scheme}:{domain}"))
428 };
429 (uri.clone(), uri, Cow::from(""))
430 },
431 )
432 .parse(i)
433}
434
435fn md_email_address(
444 i: &'_ str,
445) -> nom::IResult<&'_ str, (Cow<'_, str>, Cow<'_, str>, Cow<'_, str>)> {
446 let j = i;
447 map(
448 all_consuming(nom::sequence::separated_pair(
449 nom::bytes::complete::take_till1(|c: char| {
451 !(c.is_alphanumeric() || ".!#$%&'*+\\/=?^_`{|}~-".contains(c))
452 }),
453 tag("@"),
454 nom::bytes::complete::take_till1(|c: char| !(c.is_alphanumeric() || ".-".contains(c))),
456 )),
457 |(_, _)| {
458 (
459 Cow::Borrowed(j),
460 Cow::Owned(format!("mailto:{}", j.to_owned())),
461 Cow::Borrowed(""),
462 )
463 },
464 )
465 .parse(i)
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use nom::error::ErrorKind;
472
473 #[test]
474 fn test_md_text2dest() {
475 assert_eq!(
476 md_text2dest("[text](url)abc"),
477 Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
478 );
479 assert_eq!(
480 md_text2dest("[text[i]](url)abc"),
481 Ok((
482 "abc",
483 (Cow::from("text[i]"), Cow::from("url"), Cow::from(""))
484 ))
485 );
486 assert_eq!(
487 md_text2dest("[text[i]](ur(l))abc"),
488 Ok((
489 "abc",
490 (Cow::from("text[i]"), Cow::from("ur(l)"), Cow::from(""))
491 ))
492 );
493 assert_eq!(
494 md_text2dest("[text(url)"),
495 Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
496 );
497 assert_eq!(
498 md_text2dest("[text](<url>)abc"),
499 Ok(("abc", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
500 );
501 assert_eq!(
502 md_text2dest("[text](<url> \"link title\")abc"),
503 Ok((
504 "abc",
505 (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
506 ))
507 );
508 assert_eq!(
509 md_text2dest("[text](url \"link title\")abc"),
510 Ok((
511 "abc",
512 (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
513 ))
514 );
515 assert_eq!(
517 md_text2dest("[](./target.md)abc"),
518 Ok((
519 "abc",
520 (Cow::from(""), Cow::from("./target.md"), Cow::from(""))
521 ))
522 );
523 assert_eq!(
525 md_text2dest("[link]()abc"),
526 Ok(("abc", (Cow::from("link"), Cow::from(""), Cow::from(""))))
527 );
528 assert_eq!(
530 md_text2dest("[link](<>)abc"),
531 Ok(("abc", (Cow::from("link"), Cow::from(""), Cow::from(""))))
532 );
533 assert_eq!(
535 md_text2dest("[]()abc"),
536 Ok(("abc", (Cow::from(""), Cow::from(""), Cow::from(""))))
537 );
538 assert_eq!(
539 md_text2dest("[text]abc"),
540 Err(nom::Err::Error(nom::error::Error::new(
541 "abc",
542 ErrorKind::Tag
543 )))
544 );
545 assert_eq!(
547 md_text2dest("<a+b+c:d>abc"),
548 Ok((
549 "abc",
550 (Cow::from("a+b+c:d"), Cow::from("a+b+c:d"), Cow::from(""))
551 ))
552 );
553 assert_eq!(
555 md_text2dest("<foo@bar.example.com>abc"),
556 Ok((
557 "abc",
558 (
559 Cow::from("foo@bar.example.com"),
560 Cow::from("mailto:foo@bar.example.com"),
561 Cow::from("")
562 )
563 ))
564 );
565 assert_eq!(
566 md_text2dest("<foo.example.com>abc"),
567 Err(nom::Err::Error(nom::error::Error::new(
568 "<foo.example.com>abc",
569 ErrorKind::Tag
570 )))
571 );
572 assert_eq!(
574 md_text2dest(r#"<http://example.com?find=\*>abc"#),
575 Ok((
576 "abc",
577 (
578 Cow::from(r#"http://example.com?find=\*"#),
579 Cow::from(r#"http://example.com?find=\*"#),
580 Cow::from("")
581 )
582 ))
583 );
584 assert_eq!(
586 md_text2dest(r#"[foo](/bar\* "ti\*tle")abc"#),
587 Ok((
588 "abc",
589 (Cow::from("foo"), Cow::from("/bar*"), Cow::from("ti*tle"))
590 ))
591 );
592 }
593
594 #[test]
595 fn test_md_text2label() {
596 assert_eq!(
597 md_text2label("[link text][link label]abc"),
598 Ok(("abc", (Cow::from("link text"), Cow::from("link label"))))
599 );
600 assert_eq!(
601 md_text2label("[link text][]abc"),
602 Ok(("abc", (Cow::from("link text"), Cow::from("link text"))))
603 );
604 assert_eq!(
605 md_text2label("[link text]abc"),
606 Ok(("abc", (Cow::from("link text"), Cow::from("link text"))))
607 );
608 assert_eq!(
609 md_text2label("[]abc"),
610 Ok(("abc", (Cow::from(""), Cow::from(""))))
611 );
612 assert_eq!(
613 md_text2label(""),
614 Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
615 );
616 assert_eq!(
618 md_text2label("[text]"),
619 Ok(("", (Cow::from("text"), Cow::from("text"))))
620 );
621 assert_eq!(
622 md_text2label("[text][text]"),
623 Ok(("", (Cow::from("text"), Cow::from("text"))))
624 );
625 assert_eq!(
626 md_text2label("[text][label url"),
627 Err(nom::Err::Error(nom::error::Error::new(
628 "[label url",
629 ErrorKind::NoneOf
630 )))
631 );
632 assert_eq!(
633 md_text2label("[text](url)abc"),
634 Err(nom::Err::Error(nom::error::Error::new(
635 "(url)abc",
636 ErrorKind::NoneOf
637 )))
638 );
639 }
640
641 #[test]
642 fn test_md_label2dest() {
643 assert_eq!(
644 md_label2dest("[text]: url\nabc"),
645 Ok((
646 "\nabc",
647 (Cow::from("text"), Cow::from("url"), Cow::from(""))
648 ))
649 );
650 assert_eq!(
651 md_label2dest("[text]: url \nabc"),
652 Ok((
653 "\nabc",
654 (Cow::from("text"), Cow::from("url"), Cow::from(""))
655 ))
656 );
657 assert_eq!(
658 md_label2dest("[text]: <url url> \nabc"),
659 Ok((
660 "\nabc",
661 (Cow::from("text"), Cow::from("url url"), Cow::from(""))
662 ))
663 );
664 assert_eq!(
665 md_label2dest("[text]: url \"title\"\nabc"),
666 Ok((
667 "\nabc",
668 (Cow::from("text"), Cow::from("url"), Cow::from("title"))
669 ))
670 );
671 assert_eq!(
672 md_label2dest("[text]: url\n\"title\"\nabc"),
673 Ok((
674 "\nabc",
675 (Cow::from("text"), Cow::from("url"), Cow::from("title"))
676 ))
677 );
678 assert_eq!(
679 md_label2dest(" [text]: url\n\"title\"\nabc"),
680 Ok((
681 "\nabc",
682 (Cow::from("text"), Cow::from("url"), Cow::from("title"))
683 ))
684 );
685 assert_eq!(
686 md_label2dest("abc[text]: url\n\"title\""),
687 Err(nom::Err::Error(nom::error::Error::new(
688 "abc[text]: url\n\"title\"",
689 ErrorKind::Tag
690 )))
691 );
692 assert_eq!(
693 md_label2dest(" [text]: url\n\"title\" abc"),
694 Err(nom::Err::Error(nom::error::Error::new(
695 " [text]: url\n\"title\" abc",
696 ErrorKind::Tag
697 )))
698 );
699 assert_eq!(
701 md_label2dest("[text\\[i\\]]: ur(l)url\nabc"),
702 Ok((
703 "\nabc",
704 (Cow::from("text[i]"), Cow::from("ur(l)url"), Cow::from(""))
705 ))
706 );
707 assert_eq!(
709 md_label2dest("[text[i]]: ur(l)(url"),
710 Err(nom::Err::Error(nom::error::Error::new(
711 "[i]]: ur(l)(url",
712 ErrorKind::Tag
713 )))
714 );
715 assert_eq!(
717 md_label2dest("[text]: \nurl"),
718 Ok(("", (Cow::from("text"), Cow::from("url"), Cow::from(""))))
719 );
720 assert_eq!(
722 md_label2dest("[text]: \n\nurl"),
723 Err(nom::Err::Error(nom::error::Error::new(
724 " \n\nurl",
725 ErrorKind::Verify
726 )))
727 );
728 assert_eq!(
729 md_label2dest("[text: url"),
730 Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
731 );
732 assert_eq!(
733 md_label2dest("[text] url"),
734 Err(nom::Err::Error(nom::error::Error::new(
735 " url",
736 ErrorKind::Char
737 )))
738 );
739 assert_eq!(
740 md_label2dest("[text]: url \"link title\"\nabc"),
741 Ok((
742 "\nabc",
743 (Cow::from("text"), Cow::from("url"), Cow::from("link title"))
744 ))
745 );
746 assert_eq!(
747 md_label2dest("[text]: url \"link\ntitle\"\nabc"),
748 Ok((
749 "\nabc",
750 (
751 Cow::from("text"),
752 Cow::from("url"),
753 Cow::from("link\ntitle")
754 )
755 ))
756 );
757 assert_eq!(
758 md_label2dest("[text]: url \"link\ntitle\"abc"),
759 Err(nom::Err::Error(nom::error::Error::new(
760 "abc",
761 ErrorKind::Char
762 )))
763 );
764 assert_eq!(
765 md_label2dest("[text]:\nurl \"link\ntitle\"\nabc"),
766 Ok((
767 "\nabc",
768 (
769 Cow::from("text"),
770 Cow::from("url"),
771 Cow::from("link\ntitle")
772 )
773 ))
774 );
775 assert_eq!(
776 md_label2dest("[text]: url \"link\n\ntitle\"\nabc"),
777 Err(nom::Err::Error(nom::error::Error::new(
778 "\"link\n\ntitle\"\nabc",
779 ErrorKind::Char
780 )))
781 );
782 assert_eq!(
783 md_label2dest("[text]:\n\nurl \"link title\"\nabc"),
784 Err(nom::Err::Error(nom::error::Error::new(
785 "\n\nurl \"link title\"\nabc",
786 ErrorKind::Verify
787 )))
788 );
789 assert_eq!(
791 md_label2dest(r#"[foo]: /bar\* "ti\*tle""#),
792 Ok((
793 "",
794 (Cow::from("foo"), Cow::from("/bar*"), Cow::from("ti*tle"))
795 ))
796 );
797 }
798
799 #[test]
800 fn test_md_link_text() {
801 assert_eq!(
802 md_link_text("[text](url)"),
803 Ok(("(url)", Cow::from("text")))
804 );
805 assert_eq!(
806 md_link_text("[text[i]](url)"),
807 Ok(("(url)", Cow::from("text[i]")))
808 );
809 assert_eq!(
810 md_link_text(r#"[text\[i\]](url)"#),
811 Ok(("(url)", Cow::from("text[i]")))
812 );
813 assert_eq!(
814 md_link_text("[text(url)"),
815 Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
816 );
817 assert_eq!(
818 md_link_text(r#"[te\_xt](url)"#),
819 Ok(("(url)", Cow::from("te_xt")))
820 );
821 }
822
823 #[test]
824 fn test_md_link_label() {
825 assert_eq!(
826 md_link_label("[text]: url"),
827 Ok((": url", Cow::from("text")))
828 );
829 assert_eq!(
830 md_link_label(r#"[text\[i\]]: url"#),
831 Ok((": url", Cow::from("text[i]")))
832 );
833 assert_eq!(
834 md_link_label("[text: url"),
835 Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
836 );
837 assert_eq!(
838 md_link_label("[t[ext: url"),
839 Err(nom::Err::Error(nom::error::Error::new(
840 "[ext: url",
841 ErrorKind::Tag
842 )))
843 );
844 }
845
846 #[test]
847 fn test_md_link_destination() {
848 assert_eq!(
849 md_link_destination("url abc"),
850 Ok((" abc", Cow::from("url")))
851 );
852 assert_eq!(md_link_destination("url"), Ok(("", Cow::from("url"))));
853 assert_eq!(
854 md_link_destination("url\nabc"),
855 Ok(("\nabc", Cow::from("url")))
856 );
857 assert_eq!(
858 md_link_destination("<url>abc"),
859 Ok(("abc", Cow::from("url")))
860 );
861 assert_eq!(
862 md_link_destination(r#"<u\<r\>l>abc"#),
863 Ok(("abc", Cow::from(r#"u<r>l"#)))
864 );
865 assert_eq!(
866 md_link_destination(r#"u\)r\(l abc"#),
867 Ok((" abc", Cow::from(r#"u)r(l"#)))
868 );
869 assert_eq!(
870 md_link_destination(r#"u(r)l abc"#),
871 Ok((" abc", Cow::from(r#"u(r)l"#)))
872 );
873 assert_eq!(
874 md_link_destination("u(r)l\nabc"),
875 Ok(("\nabc", Cow::from(r#"u(r)l"#)))
876 );
877 }
878
879 #[test]
880 fn test_md_parse_link_destination() {
881 assert_eq!(md_parse_link_destination("<url>abc"), Ok(("abc", "url")));
882 assert_eq!(
883 md_parse_link_destination(r#"<u\<r\>l>abc"#),
884 Ok(("abc", r#"u\<r\>l"#))
885 );
886 assert_eq!(md_parse_link_destination("<url> abc"), Ok((" abc", "url")));
887 assert_eq!(
888 md_parse_link_destination("<url>\nabc"),
889 Ok(("\nabc", "url"))
890 );
891 assert_eq!(
892 md_parse_link_destination("<url 2>abc"),
893 Ok(("abc", "url 2"))
894 );
895 assert_eq!(md_parse_link_destination("url abc"), Ok((" abc", "url")));
896 assert_eq!(
897 md_parse_link_destination("<url(1)> abc"),
898 Ok((" abc", "url(1)"))
899 );
900 assert_eq!(
901 md_parse_link_destination(r#"<[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}> abc"#),
902 Ok((" abc", r#"[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}"#))
903 );
904 assert_eq!(
905 md_parse_link_destination("ur()l abc"),
906 Ok((" abc", "ur()l"))
907 );
908 assert_eq!(
909 md_parse_link_destination("ur()l\nabc"),
910 Ok(("\nabc", "ur()l"))
911 );
912 assert_eq!(md_parse_link_destination("<>abc"), Ok(("abc", "")));
913 assert_eq!(md_parse_link_destination("<>\nabc"), Ok(("\nabc", "")));
914 assert_eq!(md_parse_link_destination("url"), Ok(("", "url")));
915 assert_eq!(md_parse_link_destination(""), Ok(("", "")));
916 assert_eq!(md_parse_link_destination("\nabc"), Ok(("\nabc", "")));
917 }
918
919 #[test]
920 fn test_md_escaped_str_transform() {
921 assert_eq!(md_escaped_str_transform(""), Ok(("", Cow::from(""))));
922 assert_eq!(md_escaped_str_transform(" "), Ok(("", Cow::from(" "))));
924 assert_eq!(
925 md_escaped_str_transform(r#"abc`:<>abc"#),
926 Ok(("", Cow::from(r#"abc`:<>abc"#)))
927 );
928 assert_eq!(
929 md_escaped_str_transform(r#"\<\>\\"#),
930 Ok(("", Cow::from(r#"<>\"#)))
931 );
932 assert_eq!(
933 md_escaped_str_transform(r#"\(\)\\"#),
934 Ok(("", Cow::from(r#"()\"#)))
935 );
936 assert_eq!(
938 md_escaped_str_transform(
939 r#"\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~"#
940 ),
941 Ok(("", Cow::from(r###"!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"###)))
942 );
943 }
944
945 #[test]
946 fn test_md_link_title() {
947 assert_eq!(
950 md_link_title(" (title)abc"),
951 Ok(("abc", Cow::from("title")))
952 );
953 assert_eq!(
954 md_link_title(" (ti(t)le)abc"),
955 Ok(("abc", Cow::from("ti(t)le")))
956 );
957 assert_eq!(
958 md_link_title(r#" (ti\(t\)le)abc"#),
959 Ok(("abc", Cow::from("ti(t)le")))
960 );
961 assert_eq!(
962 md_link_title(r#" "1\\23\"4\'56"abc"#),
963 Ok(("abc", Cow::from(r#"1\23"4'56"#)))
964 );
965 assert_eq!(
966 md_link_title(" \"tu\nvwxy\"abc"),
967 Ok(("abc", Cow::from("tu\nvwxy")))
968 );
969 assert_eq!(
970 md_link_title(" 'tu\nv\\\'wxy'abc"),
971 Ok(("abc", Cow::from("tu\nv\'wxy")))
972 );
973 assert_eq!(
974 md_link_title(" (ti\n\ntle)abc"),
975 Err(nom::Err::Error(nom::error::Error::new(
976 "(ti\n\ntle)abc",
977 ErrorKind::Verify
978 )))
979 );
980 }
981
982 #[test]
983 fn test_md_parse_link_title() {
984 assert_eq!(md_parse_link_title(" (title)abc"), Ok(("abc", "title")));
985 assert_eq!(md_parse_link_title(" (ti(t)le)abc"), Ok(("abc", "ti(t)le")));
986 assert_eq!(
987 md_parse_link_title(r#" "1\\23\"4\'56"abc"#),
988 Ok(("abc", r#"1\\23\"4\'56"#))
989 );
990 assert_eq!(
991 md_parse_link_title(" \"tu\nvwxy\"abc"),
992 Ok(("abc", "tu\nvwxy"))
993 );
994 assert_eq!(
995 md_parse_link_title(" 'tu\nv\\\'wxy'abc"),
996 Ok(("abc", "tu\nv\\\'wxy"))
997 );
998 assert_eq!(
999 md_parse_link_title(" (ti\n\ntle)abc"),
1000 Err(nom::Err::Error(nom::error::Error::new(
1001 "(ti\n\ntle)abc",
1002 ErrorKind::Verify
1003 )))
1004 );
1005 }
1006 #[test]
1007 fn test_md_absolute_uri() {
1008 assert_eq!(
1009 md_absolute_uri("http://domain.com").unwrap().1.0,
1010 Cow::Borrowed("http://domain.com")
1011 );
1012 assert_eq!(
1013 md_absolute_uri("http://domain.com").unwrap().1.1,
1014 Cow::Borrowed("http://domain.com")
1015 );
1016 assert_eq!(
1017 md_absolute_uri("scheme:domain").unwrap().1.1,
1018 Cow::Borrowed("scheme:domain")
1019 );
1020 assert_eq!(
1021 md_absolute_uri("scheme:domain abc"),
1022 Err(nom::Err::Error(nom::error::Error::new(
1023 " abc",
1024 ErrorKind::Eof
1025 )))
1026 );
1027 assert_eq!(
1028 md_absolute_uri("h:domain"),
1029 Err(nom::Err::Error(nom::error::Error::new(
1030 "h:domain",
1031 ErrorKind::Verify
1032 )))
1033 );
1034 assert_eq!(
1035 md_absolute_uri("sche&me:domain"),
1036 Err(nom::Err::Error(nom::error::Error::new(
1037 "&me:domain",
1038 ErrorKind::Tag
1039 )))
1040 );
1041 assert_eq!(
1042 md_absolute_uri("scheme+much+too.long......................:uri"),
1043 Err(nom::Err::Error(nom::error::Error::new(
1044 "scheme+much+too.long......................:uri",
1045 ErrorKind::Verify
1046 )))
1047 );
1048 assert_eq!(
1049 md_absolute_uri("httpÜ:domain abc"),
1050 Err(nom::Err::Error(nom::error::Error::new(
1051 "Ü:domain abc",
1052 ErrorKind::Tag
1053 )))
1054 );
1055 assert_eq!(
1056 md_absolute_uri("no colon"),
1057 Err(nom::Err::Error(nom::error::Error::new(
1058 " colon",
1059 ErrorKind::Tag
1060 )))
1061 );
1062 assert_eq!(
1063 md_absolute_uri("scheme:domai>n"),
1064 Err(nom::Err::Error(nom::error::Error::new(
1065 ">n",
1066 ErrorKind::Eof
1067 )))
1068 );
1069
1070 let res = md_absolute_uri("scheme:domain").unwrap();
1071 assert!(matches!(res.1.0, Cow::Borrowed(..)));
1072 assert_eq!(res.1.0, Cow::from("scheme:domain"));
1073
1074 let res = md_absolute_uri("scheme:domai%25n").unwrap();
1075 assert!(matches!(res.1.0, Cow::Owned(..)));
1076 assert_eq!(res.1.0, Cow::from("scheme:domai%n"));
1077 }
1078
1079 #[test]
1080 fn test_md_email_address() {
1081 let res = md_email_address("local@domain").unwrap();
1082 assert!(matches!(res.1.0, Cow::Borrowed(..)));
1083 assert!(matches!(res.1.1, Cow::Owned(..)));
1084 assert_eq!(res.1.0, Cow::from("local@domain"));
1085 assert_eq!(res.1.1, Cow::from("mailto:local@domain"));
1086
1087 let res = md_email_address("localÜ@domainÜ").unwrap();
1088 assert!(matches!(res.1.0, Cow::Borrowed(..)));
1089 assert!(matches!(res.1.1, Cow::Owned(..)));
1090 assert_eq!(res.1.0, Cow::from("localÜ@domainÜ"));
1091 assert_eq!(res.1.1, Cow::from("mailto:localÜ@domainÜ"));
1092
1093 let res = md_email_address("lo.cal@domain").unwrap();
1094 assert!(matches!(res.1.0, Cow::Borrowed(..)));
1095 assert!(matches!(res.1.1, Cow::Owned(..)));
1096 assert_eq!(res.1.0, Cow::from("lo.cal@domain"));
1097 assert_eq!(res.1.1, Cow::from("mailto:lo.cal@domain"));
1098
1099 assert_eq!(
1100 md_email_address("lo_cal@do_main"),
1101 Err(nom::Err::Error(nom::error::Error::new(
1102 "_main",
1103 ErrorKind::Eof
1104 )))
1105 );
1106 }
1107
1108 }