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