///|
/// `autolink_email(s, last, start)` matches an email autolink starting at
/// `start` in the range `[start;last]` (assumed on the same line).
pub fn autolink_email(
s : String,
last~ : Int32 = -1,
start~ : Int32 = 0,
) -> Last? {
let last = if last < 0 { s.length() + last } else { last }
fn char_is_alphanum_or_hyp(c) {
@char.is_ascii_alphanum(c) || c == '-'
}
fn label_seq(last : Int32, k : Int32) -> Int32? {
guard k <= last && s[k].to_char() is Some(c) && @char.is_ascii_alphanum(c) else {
return None
}
for c = 1, k = k + 1 {
guard k <= last else { break None }
let sk = s[k].unsafe_to_char()
if char_is_alphanum_or_hyp(sk) && c <= 63 {
continue c + 1, k + 1
}
guard c <= 63 &&
s[k - 1].to_char() is Some(c) &&
@char.is_ascii_alphanum(c) else {
break None
}
break match sk {
'>' => Some(k)
'.' => label_seq(last, k + 1)
_ => None
}
}
}
guard start <= last && s[start] == '<' else { None }
for k in (start + 1)..<=last {
let sk = s[k].unsafe_to_char()
if char_is_atext_plus_dot(sk) {
continue
}
if sk == '@' && s[k - 1].to_char() is Some(c) && char_is_atext_plus_dot(c) {
break label_seq(last, k + 1)
}
break None
} nobreak {
None
}
}
///|
/// `autolink_uri(s, last, start)` matches an URI autolink starting at
/// `start` in the range `[start;last]` (assumed on the same line).
pub fn autolink_uri(
s : String,
last~ : Int32 = -1,
start~ : Int32 = 0,
) -> Last? {
let last = if last < 0 { s.length() + last } else { last }
let char_is_scheme = fn(c) {
@char.is_ascii_alphanum(c) || c is ('+' | '-' | '.')
}
// Code units outside the excluded ASCII set are URI chars; surrogate
// halves belong to non-ASCII chars, which are allowed.
let unit_is_uri = fn(u : UInt16) {
match u.to_char() {
Some(c) => !(c is ('\u{0}'..<='\u{1f}' | '\u{7f}' | '<' | '>' | ' '))
None => true
}
}
let rest = fn(s : String, last, k) {
for k in k..<=last {
let u = s[k]
guard !unit_is_uri(u) else { continue }
break if u == '>' { Some(k) } else { None }
} nobreak {
None
}
}
let next = start + 1
guard next <= last &&
s[start] == '<' &&
s[next].to_char() is Some(first_scheme_char) &&
first_scheme_char.is_ascii_alphabetic() else {
return None
}
for c = 1, k = next + 1 {
guard k <= last else { break None }
guard s[k].to_char() is Some(sk) else { break None }
if char_is_scheme(sk) && c <= 32 {
continue c + 1, k + 1
}
if sk == ':' && 2 <= c && c <= 32 {
break rest(s, last, k + 1)
}
break None
}
}