diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 4669295..46319f9 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -3029,7 +3029,7 @@ fn compare_angle_link<'b, 's>( ), ( EmacsField::Required(":format"), - |_| Some("plain"), + |_| Some("angle"), compare_property_unquoted_atom ), ( diff --git a/src/parser/angle_link.rs b/src/parser/angle_link.rs index c1e6c3f..2b92e48 100644 --- a/src/parser/angle_link.rs +++ b/src/parser/angle_link.rs @@ -1,21 +1,29 @@ +use nom::branch::alt; use nom::bytes::complete::tag; -use nom::character::complete::anychar; +use nom::bytes::complete::take_until; +use nom::combinator::consumed; +use nom::combinator::map; +use nom::combinator::map_parser; +use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; -use nom::multi::many_till; +use nom::combinator::rest; +use nom::sequence::tuple; use super::org_source::OrgSource; use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting; +use super::util::text_until_exit; use crate::context::parser_with_context; use crate::context::ContextElement; use crate::context::ExitClass; use crate::context::ExitMatcherNode; use crate::context::RefContext; use crate::error::Res; +use crate::parser::plain_link::parse_file_and_application; use crate::parser::plain_link::protocol; -use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::types::AngleLink; +use crate::types::LinkType; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn angle_link<'b, 'g, 'r, 's>( @@ -23,9 +31,14 @@ pub(crate) fn angle_link<'b, 'g, 'r, 's>( input: OrgSource<'s>, ) -> Res, AngleLink<'s>> { let (remaining, _) = tag("<")(input)?; - let (remaining, proto) = protocol(context, remaining)?; - let (remaining, _separator) = tag(":")(remaining)?; - let (remaining, path) = path_angle(context, remaining)?; + 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)?; let (remaining, _) = tag(">")(remaining)?; let (remaining, _trailing_whitespace) = maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; @@ -34,15 +47,23 @@ pub(crate) fn angle_link<'b, 'g, 'r, 's>( remaining, AngleLink { source: source.into(), - link_type: todo!(), - path: path.into(), - raw_link: todo!(), - search_option: todo!(), - application: todo!(), + 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, }, )) } +#[derive(Debug)] +struct PathAngle<'s> { + link_type: LinkType<'s>, + path: &'s str, + search_option: Option<&'s str>, + application: Option<&'s str>, +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn path_angle<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, @@ -54,9 +75,7 @@ fn path_angle<'b, 'g, 'r, 's>( }); let parser_context = context.with_additional_node(&parser_context); - let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - - let (remaining, path) = recognize(many_till(anychar, peek(exit_matcher)))(input)?; + let (remaining, path) = text_until_exit(&parser_context, input)?; Ok((remaining, path)) } @@ -67,3 +86,69 @@ fn path_angle_end<'b, 'g, 'r, 's>( ) -> Res, OrgSource<'s>> { tag(">")(input) } + +#[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, 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, 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, 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, PathAngle<'s>> { + let (remaining, link_type) = map( + tuple((parser_with_context!(protocol)(context), tag(":"))), + |(protocol, _)| LinkType::Protocol(protocol.into()), + )(input)?; + 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, + path: path.into(), + search_option: search_option.map(Into::<&str>::into), + application: None, + }, + )) +}