use nom::branch::alt; use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::bytes::complete::take_while1; use nom::character::complete::anychar; use nom::character::complete::line_ending; use nom::character::complete::space0; use nom::character::complete::space1; use nom::combinator::eof; use nom::combinator::not; use nom::combinator::peek; use nom::combinator::recognize; use nom::multi::many_till; use nom::sequence::tuple; use super::org_source::BracketDepth; use super::org_source::OrgSource; use crate::error::CustomError; use crate::error::MyError; use crate::error::Res; use crate::parser::util::start_of_line; use crate::parser::Keyword; const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [ "caption", "data", "header", "headers", "label", "name", "plot", "resname", "result", "results", "source", "srcname", "tblname", ]; const ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["caption", "results"]; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn keyword<'r, 's>( _context: RefContext<'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { start_of_line(input)?; // TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references. let (remaining, rule) = recognize(tuple(( space0, tag("#+"), not(peek(tag_no_case("call"))), not(peek(tag_no_case("begin"))), is_not(" \t\r\n:"), tag(":"), alt((recognize(tuple((space1, is_not("\r\n")))), space0)), alt((line_ending, eof)), )))(input)?; Ok(( remaining, Keyword { source: rule.into(), }, )) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn affiliated_keyword<'r, 's>( _context: RefContext<'r, 's>, input: OrgSource<'s>, ) -> Res, Keyword<'s>> { start_of_line(input)?; // TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references. let (remaining, rule) = recognize(tuple(( space0, tag("#+"), affiliated_key, tag(":"), alt((recognize(tuple((space1, is_not("\r\n")))), space0)), alt((line_ending, eof)), )))(input)?; Ok(( remaining, Keyword { source: rule.into(), }, )) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn affiliated_key<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { alt(( recognize(tuple((dual_affiliated_key, tag("["), optval, tag("]")))), plain_affiliated_key, export_keyword, ))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS { let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input); match result { Ok((remaining, ent)) => { return Ok((remaining, ent)); } Err(_) => {} } } Err(nom::Err::Error(CustomError::MyError(MyError( "NoKeywordKey".into(), )))) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { for keyword in ORG_ELEMENT_DUAL_KEYWORDS { let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input); match result { Ok((remaining, ent)) => { return Ok((remaining, ent)); } Err(_) => {} } } Err(nom::Err::Error(CustomError::MyError(MyError( "NoKeywordKey".into(), )))) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn optval<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { recognize(many_till( anychar, peek(optval_end(input.get_bracket_depth())), ))(input) } const fn optval_end( starting_bracket_depth: BracketDepth, ) -> impl for<'s> Fn(OrgSource<'s>) -> Res, OrgSource<'s>> { move |input: OrgSource<'_>| _optval_end(input, starting_bracket_depth) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _optval_end<'s>( input: OrgSource<'s>, starting_bracket_depth: BracketDepth, ) -> Res, OrgSource<'s>> { let current_depth = input.get_bracket_depth() - starting_bracket_depth; if current_depth < 0 { // This shouldn't be possible because if depth is 0 then a closing bracket should end the opval. unreachable!("Exceeded optval bracket depth.") } if current_depth == 0 { let close_bracket = tag::<_, _, CustomError<_>>("]")(input); if close_bracket.is_ok() { return close_bracket; } } tag("\n")(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn export_keyword<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { recognize(tuple(( tag_no_case("attr_"), take_while1(|c: char| c.is_alphanumeric() || "-_".contains(c)), )))(input) }