124 lines
4.6 KiB
Rust
124 lines
4.6 KiB
Rust
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::bytes::complete::tag_no_case;
|
|
use nom::character::complete::anychar;
|
|
use nom::character::complete::none_of;
|
|
use nom::character::complete::one_of;
|
|
use nom::combinator::eof;
|
|
use nom::combinator::peek;
|
|
use nom::combinator::recognize;
|
|
use nom::multi::many_till;
|
|
|
|
use super::Context;
|
|
use crate::error::CustomError;
|
|
use crate::error::MyError;
|
|
use crate::error::Res;
|
|
use crate::parser::exiting::ExitClass;
|
|
use crate::parser::object::PlainLink;
|
|
use crate::parser::parser_context::ContextElement;
|
|
use crate::parser::parser_context::ExitMatcherNode;
|
|
use crate::parser::parser_with_context::parser_with_context;
|
|
use crate::parser::util::exit_matcher_parser;
|
|
use crate::parser::util::get_consumed;
|
|
use crate::parser::util::get_one_before;
|
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
pub fn plain_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainLink<'s>> {
|
|
let (remaining, _) = pre(context, input)?;
|
|
let (remaining, proto) = protocol(context, remaining)?;
|
|
let (remaining, _separator) = tag(":")(remaining)?;
|
|
let (remaining, path) = path_plain(context, remaining)?;
|
|
peek(parser_with_context!(post)(context))(remaining)?;
|
|
let source = get_consumed(input, remaining);
|
|
Ok((
|
|
remaining,
|
|
PlainLink {
|
|
source,
|
|
link_type: proto,
|
|
path,
|
|
},
|
|
))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
|
|
let document_root = context.get_document_root().unwrap();
|
|
let preceding_character = get_one_before(document_root, input)
|
|
.map(|slice| slice.chars().next())
|
|
.flatten();
|
|
match preceding_character {
|
|
// If None, we are at the start of the file which is fine
|
|
None => {}
|
|
Some(x) if !WORD_CONSTITUENT_CHARACTERS.contains(x) => {}
|
|
Some(_) => {
|
|
// Not at start of line, cannot be a heading
|
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
"Not a valid pre character for plain link.",
|
|
))));
|
|
}
|
|
};
|
|
Ok((input, ()))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
|
|
let (remaining, _) = alt((eof, recognize(none_of(WORD_CONSTITUENT_CHARACTERS))))(input)?;
|
|
Ok((remaining, ()))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
pub fn protocol<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
|
// TODO: This should be defined by org-link-parameters
|
|
let (remaining, proto) = alt((
|
|
alt((
|
|
tag_no_case("id"),
|
|
tag_no_case("eww"),
|
|
tag_no_case("rmail"),
|
|
tag_no_case("mhe"),
|
|
tag_no_case("irc"),
|
|
tag_no_case("info"),
|
|
tag_no_case("gnus"),
|
|
tag_no_case("docview"),
|
|
tag_no_case("bibtex"),
|
|
tag_no_case("bbdb"),
|
|
tag_no_case("w3m"),
|
|
)),
|
|
alt((
|
|
tag_no_case("doi"),
|
|
tag_no_case("file+sys"),
|
|
tag_no_case("file+emacs"),
|
|
tag_no_case("shell"),
|
|
tag_no_case("news"),
|
|
tag_no_case("mailto"),
|
|
tag_no_case("https"),
|
|
tag_no_case("http"),
|
|
tag_no_case("ftp"),
|
|
tag_no_case("help"),
|
|
tag_no_case("file"),
|
|
tag_no_case("elisp"),
|
|
)),
|
|
))(input)?;
|
|
Ok((remaining, proto))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn path_plain<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
|
// TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring"
|
|
let parser_context =
|
|
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
|
class: ExitClass::Beta,
|
|
exit_matcher: &path_plain_end,
|
|
}));
|
|
|
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
|
|
|
let (remaining, path) = recognize(many_till(anychar, peek(exit_matcher)))(input)?;
|
|
Ok((remaining, path))
|
|
}
|
|
|
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
fn path_plain_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
|
recognize(one_of(" \t\r\n()[]<>"))(input)
|
|
}
|