use super::prelude::*;
use crate::tree::{AnchorTarget, LinkLabel};
pub const RULE_LINK_SINGLE: Rule = Rule {
name: "link-single",
try_consume_fn: link,
};
pub const RULE_LINK_SINGLE_NEW_TAB: Rule = Rule {
name: "link-single-new-tab",
try_consume_fn: link_new_tab,
};
fn link<'p, 'r, 't>(
log: &slog::Logger,
parser: &'p mut Parser<'r, 't>,
) -> ParseResult<'r, 't, Elements<'t>> {
trace!(log, "Trying to create a single-bracket link (regular)");
check_step(parser, Token::LeftBracket)?;
try_consume_link(log, parser, RULE_LINK_SINGLE, AnchorTarget::Same)
}
fn link_new_tab<'p, 'r, 't>(
log: &slog::Logger,
parser: &'p mut Parser<'r, 't>,
) -> ParseResult<'r, 't, Elements<'t>> {
trace!(log, "Trying to create a single-bracket link (new tab)");
check_step(parser, Token::LeftBracketSpecial)?;
try_consume_link(log, parser, RULE_LINK_SINGLE_NEW_TAB, AnchorTarget::NewTab)
}
fn try_consume_link<'p, 'r, 't>(
log: &slog::Logger,
parser: &'p mut Parser<'r, 't>,
rule: Rule,
target: AnchorTarget,
) -> ParseResult<'r, 't, Elements<'t>> {
debug!(log, "Trying to create a single-bracket link"; "target" => target.name());
let url = collect_text(
log,
parser,
rule,
&[ParseCondition::current(Token::Whitespace)],
&[
ParseCondition::current(Token::RightBracket),
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::LineBreak),
],
None,
)?;
if !url_valid(url) {
return Err(parser.make_warn(ParseWarningKind::InvalidUrl));
}
debug!(
log,
"Retrieved URL for link, now fetching label";
"url" => url,
);
let label = collect_text(
log,
parser,
rule,
&[ParseCondition::current(Token::RightBracket)],
&[
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::LineBreak),
],
None,
)?;
debug!(
log,
"Retrieved label for link, now build element";
"label" => label,
);
let label = label.trim();
let element = Element::Link {
url: cow!(url),
label: LinkLabel::Text(cow!(label)),
target,
};
ok!(element)
}
fn url_valid(url: &str) -> bool {
const SCHEMES: [&str; 20] = [
"blob:",
"chrome-extension://",
"chrome://",
"content://",
"data:",
"dns:",
"feed:",
"file://",
"ftp://",
"git://",
"gopher://",
"http://",
"https://",
"irc6://",
"irc://",
"ircs://",
"mailto:",
"resource://",
"rtmp://",
"sftp://",
];
if url.is_empty() {
return false;
}
if url.starts_with('/') {
return true;
}
for scheme in &SCHEMES {
if url.starts_with(scheme) {
return true;
}
}
false
}