use super::prelude::*;
use crate::tree::{AnchorTarget, LinkLabel};
use std::borrow::Cow;
pub const RULE_LINK_TRIPLE: Rule = Rule {
name: "link-triple",
try_consume_fn: link,
};
pub const RULE_LINK_TRIPLE_NEW_TAB: Rule = Rule {
name: "link-triple-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 triple-bracket link (regular)");
check_step(parser, Token::LeftLink)?;
try_consume_link(log, parser, RULE_LINK_TRIPLE, 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 triple-bracket link (new tab)");
check_step(parser, Token::LeftLinkSpecial)?;
try_consume_link(log, parser, RULE_LINK_TRIPLE_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 triple-bracket link"; "target" => target.name());
let (url, last) = collect_text_keep(
log,
parser,
rule,
&[
ParseCondition::current(Token::Pipe),
ParseCondition::current(Token::RightLink),
],
&[
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::LineBreak),
],
None,
)?;
debug!(
log,
"Retrieved url for link, now build element";
"url" => url,
);
let url = url.trim();
if url.is_empty() {
return Err(parser.make_warn(ParseWarningKind::RuleFailed));
}
match last.token {
Token::RightLink => build_same(log, parser, url, target),
Token::Pipe => build_separate(log, parser, rule, url, target),
_ => unreachable!(),
}
}
fn build_same<'p, 'r, 't>(
log: &slog::Logger,
_parser: &'p mut Parser<'r, 't>,
url: &'t str,
target: AnchorTarget,
) -> ParseResult<'r, 't, Elements<'t>> {
debug!(
log,
"Building link with same URL and label";
"url" => url,
);
let label = strip_category(url).map(Cow::Borrowed);
let element = Element::Link {
url: cow!(url),
label: LinkLabel::Url(label),
target,
};
ok!(element)
}
fn build_separate<'p, 'r, 't>(
log: &slog::Logger,
parser: &'p mut Parser<'r, 't>,
rule: Rule,
url: &'t str,
target: AnchorTarget,
) -> ParseResult<'r, 't, Elements<'t>> {
debug!(
log,
"Building link with separate URL and label";
"url" => url,
);
let label = collect_text(
log,
parser,
rule,
&[ParseCondition::current(Token::RightLink)],
&[
ParseCondition::current(Token::ParagraphBreak),
ParseCondition::current(Token::LineBreak),
],
None,
)?;
debug!(
log,
"Retrieved label for link, now building element";
"label" => label,
);
let label = label.trim();
let label = if label.is_empty() {
LinkLabel::Page
} else {
LinkLabel::Text(cow!(label))
};
let element = Element::Link {
url: cow!(url),
label,
target,
};
ok!(element)
}
fn strip_category(url: &str) -> Option<&str> {
url.find(':').map(|idx| url[idx + 1..].trim_start())
}
#[test]
fn test_strip_category() {
macro_rules! check {
($input:expr, $expected:expr $(,)?) => {{
let actual = strip_category($input);
assert_eq!(
actual, $expected,
"Actual stripped URL label doesn't match expected",
);
}};
}
check!("", None);
check!("scp-001", None);
check!("Guide Hub", None);
check!("theme:just-girly-things", Some("just-girly-things"));
check!("theme: just-girly-things", Some("just-girly-things"));
check!("theme: Just Girly Things", Some("Just Girly Things"));
check!("component:fancy-sidebar", Some("fancy-sidebar"));
check!("component:Fancy Sidebar", Some("Fancy Sidebar"));
check!("component: Fancy Sidebar", Some("Fancy Sidebar"));
check!(
"multiple:categories:here:test",
Some("categories:here:test"),
);
check!(
"multiple: categories: here: test",
Some("categories: here: test"),
);
}