organic/src/parser/angle_link.rs

160 lines
5.1 KiB
Rust
Raw Normal View History

2023-10-08 16:46:18 +00:00
use nom::branch::alt;
2023-07-14 03:02:12 +00:00
use nom::bytes::complete::tag;
use nom::bytes::complete::take;
2023-10-08 16:46:18 +00:00
use nom::bytes::complete::take_until;
use nom::combinator::consumed;
use nom::combinator::flat_map;
2023-10-08 16:46:18 +00:00
use nom::combinator::map;
use nom::combinator::map_parser;
use nom::combinator::opt;
2023-07-14 03:02:12 +00:00
use nom::combinator::peek;
use nom::combinator::recognize;
2023-10-08 16:46:18 +00:00
use nom::combinator::rest;
use nom::combinator::verify;
use nom::multi::many1_count;
2023-10-08 16:46:18 +00:00
use nom::sequence::tuple;
2023-07-14 03:02:12 +00:00
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
2023-10-08 16:46:18 +00:00
use super::util::text_until_exit;
2023-09-03 16:07:51 +00:00
use crate::context::parser_with_context;
use crate::context::ContextElement;
use crate::context::ExitClass;
use crate::context::ExitMatcherNode;
use crate::context::RefContext;
2023-07-14 02:42:42 +00:00
use crate::error::Res;
2023-10-08 16:46:18 +00:00
use crate::parser::plain_link::parse_file_and_application;
2023-07-14 03:02:12 +00:00
use crate::parser::plain_link::protocol;
use crate::parser::util::get_consumed;
2023-09-03 16:07:51 +00:00
use crate::types::AngleLink;
2023-10-08 16:46:18 +00:00
use crate::types::LinkType;
2023-07-14 02:42:42 +00:00
2023-08-11 00:04:59 +00:00
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
2023-09-11 17:13:28 +00:00
pub(crate) fn angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, AngleLink<'s>> {
2023-07-14 03:02:12 +00:00
let (remaining, _) = tag("<")(input)?;
2023-10-08 16:46:18 +00:00
let (remaining, (raw_link, parsed_link)) = consumed(map_parser(
recognize(tuple((
parser_with_context!(protocol)(context),
tag(":"),
parser_with_context!(path_angle)(context),
))),
parser_with_context!(parse_angle_link)(context),
))(remaining)?;
2023-07-14 03:02:12 +00:00
let (remaining, _) = tag(">")(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
2023-07-14 03:02:12 +00:00
let source = get_consumed(input, remaining);
Ok((
remaining,
AngleLink {
source: source.into(),
2023-10-08 16:46:18 +00:00
link_type: parsed_link.link_type,
path: parsed_link.path,
raw_link: raw_link.into(),
search_option: parsed_link.search_option,
application: parsed_link.application,
2023-07-14 03:02:12 +00:00
},
))
}
2023-10-08 16:46:18 +00:00
#[derive(Debug)]
struct PathAngle<'s> {
link_type: LinkType<'s>,
path: &'s str,
search_option: Option<&'s str>,
application: Option<&'s str>,
}
2023-08-11 00:04:59 +00:00
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
2023-09-03 16:07:51 +00:00
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &path_angle_end,
});
let parser_context = context.with_additional_node(&parser_context);
2023-07-14 03:02:12 +00:00
2023-10-08 16:46:18 +00:00
let (remaining, path) = text_until_exit(&parser_context, input)?;
2023-07-14 03:02:12 +00:00
Ok((remaining, path))
}
2023-08-11 00:04:59 +00:00
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
2023-07-14 03:02:12 +00:00
tag(">")(input)
2023-07-14 02:42:42 +00:00
}
2023-10-08 16:46:18 +00:00
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parse_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathAngle<'s>> {
alt((
parser_with_context!(parse_file_angle_link)(context),
parser_with_context!(parse_protocol_angle_link)(context),
))(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parse_file_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathAngle<'s>> {
let (remaining, application) = map(
tuple((
peek(tag("file")),
map_parser(
parser_with_context!(protocol)(context),
parse_file_and_application,
),
tag(":"),
)),
|(_, application, _)| application,
)(input)?;
let (remaining, _) = opt(flat_map(
peek(map(verify(many1_count(tag("/")), |c| *c >= 3), |c| c - 1)),
take,
))(remaining)?;
2023-10-08 16:46:18 +00:00
let (remaining, path) = alt((take_until("::"), rest))(remaining)?;
let (remaining, search_option) = opt(map(tuple((tag("::"), rest)), |(_, search_option)| {
search_option
}))(remaining)?;
Ok((
remaining,
PathAngle {
link_type: LinkType::File,
path: path.into(),
search_option: search_option.map(Into::<&str>::into),
application: application.map(Into::<&str>::into),
},
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parse_protocol_angle_link<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathAngle<'s>> {
let (remaining, link_type) = map(
tuple((parser_with_context!(protocol)(context), tag(":"))),
|(protocol, _)| LinkType::Protocol(protocol.into()),
)(input)?;
let (remaining, path) = rest(remaining)?;
2023-10-08 16:46:18 +00:00
Ok((
remaining,
PathAngle {
link_type,
path: path.into(),
search_option: None,
2023-10-08 16:46:18 +00:00
application: None,
},
))
}