sable-markdown/src/parser/inline/autolink.rs@main
raw
1use nom::{
2 IResult, Parser,
3 branch::alt,
4 bytes::complete::{take_while, take_while1},
5 character::complete::{char, satisfy},
6 combinator::{map, recognize},
7 sequence::{delimited, pair, terminated},
8};
9
10pub(super) fn autolink(input: &str) -> IResult<&str, String> {
11 delimited(char('<'), alt((uri, email)), char('>')).parse(input)
12}
13
14fn uri(input: &str) -> IResult<&str, String> {
16 map(
17 pair(
18 terminated(scheme, char(':')),
19 take_while(|c: char| {
20 !c.is_ascii_control() && !c.is_ascii_whitespace() && c != '<' && c != '>'
21 }),
22 ),
23 |(scheme_part, rest): (&str, &str)| {
24 let mut s = String::from(scheme_part);
25 s.push(':');
26 s.push_str(rest);
27 s
28 },
29 )
30 .parse(input)
31}
32
33fn email(input: &str) -> IResult<&str, String> {
35 map(
36 recognize(pair(
37 take_while1(|c: char| c.is_ascii_alphanumeric() || "_-.".contains(c)),
38 pair(
39 char('@'),
40 take_while1(|c: char| c.is_ascii_alphanumeric() || ".-".contains(c)),
41 ),
42 )),
43 |v: &str| v.to_string(),
44 )
45 .parse(input)
46}
47
48fn scheme(input: &str) -> IResult<&str, &str> {
50 recognize(pair(satisfy(is_scheme_start), take_while1(is_scheme_char))).parse(input)
51}
52
53const fn is_scheme_char(c: char) -> bool {
54 c.is_ascii_alphanumeric() || matches!(c, '+' | '.' | '-')
55}
56
57const fn is_scheme_start(c: char) -> bool {
58 c.is_ascii_alphabetic()
59}
60