use std::rc::Rc; use nom::combinator::eof; use nom::IResult; use super::error::CustomError; use super::error::MyError; use super::error::Res; use super::list::List; use super::list::Node; use super::Context; type Matcher = dyn for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str>; #[derive(Debug, Clone)] pub struct ContextTree<'r, 's> { tree: List>, } impl<'r, 's> ContextTree<'r, 's> { pub fn new() -> Self { ContextTree { tree: List::new() } } pub fn ptr_eq<'x, 'y>(&self, other: &ContextTree<'x, 'y>) -> bool { self.tree.ptr_eq(&other.tree) } pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> ContextTree<'r, 's> { let new_list = self.tree.push_front(data); ContextTree { tree: new_list } } pub fn pop_front(&mut self) -> (Option>, ContextTree<'r, 's>) { let (popped_element, remaining) = self.tree.pop_front(); (popped_element, ContextTree { tree: remaining }) } pub fn iter(&self) -> impl Iterator>>> { self.tree.iter() } pub fn iter_until<'x: 'r>( &'r self, other: &'x ContextTree<'x, 's>, ) -> impl Iterator>>> { self.tree.iter_until(&other.tree) } pub fn into_iter_until<'x: 'r>( self, other: &'x ContextTree<'x, 's>, ) -> impl Iterator> { self.tree.into_iter_until(&other.tree) } #[tracing::instrument(ret, level = "debug")] pub fn check_exit_matcher( &'r self, i: &'s str, ) -> IResult<&'s str, &'s str, CustomError<&'s str>> { // Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run. let at_end_of_file = eof(i); if at_end_of_file.is_ok() { return at_end_of_file; } // let blocked_context = // self.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { // exit_matcher: ChainBehavior::IgnoreParent(Some(&always_fail)), // })); for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { ContextElement::ExitMatcherNode(exit_matcher) => { match exit_matcher.exit_matcher { ChainBehavior::AndParent(Some(matcher)) => { let local_result = matcher(self, i); if local_result.is_ok() { return local_result; } } ChainBehavior::AndParent(None) => {} ChainBehavior::IgnoreParent(Some(matcher)) => { let local_result = matcher(self, i); if local_result.is_ok() { return local_result; } // TODO: Make this a specific error instead of just a generic MyError return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); } ChainBehavior::IgnoreParent(None) => { // TODO: Make this a specific error instead of just a generic MyError return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); } }; } _ => {} }; } // TODO: Make this a specific error instead of just a generic MyError return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); } pub fn get_document_root(&self) -> Option<&'s str> { for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { ContextElement::DocumentRoot(body) => { return Some(body); } _ => {} } } None } } #[derive(Debug)] pub enum ContextElement<'r, 's> { /// Stores a reference to the entire org-mode document being parsed. /// /// This is used for look-behind. DocumentRoot(&'s str), ExitMatcherNode(ExitMatcherNode<'r>), Context(&'r str), /// Stores the indentation level of the current list item ListItem(usize), /// Stores the name of the greater block GreaterBlock(&'s str), } #[derive(Debug)] pub struct ExitMatcherNode<'r> { pub exit_matcher: ChainBehavior<'r>, } #[derive(Clone)] pub enum ChainBehavior<'r> { AndParent(Option<&'r Matcher>), IgnoreParent(Option<&'r Matcher>), } impl<'r> std::fmt::Debug for ChainBehavior<'r> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ChainBehavior"); // match self { // ChainBehavior::AndParent(_) => { // formatter = formatter.field("type", &"AndParent"); // } // ChainBehavior::IgnoreParent(_) => { // formatter = formatter.field("type", &"IgnoreParent"); // } // }; formatter.finish() } }