use nom::combinator::eof; use super::exiting::ExitClass; use super::global_settings::GlobalSettings; use super::list::List; use super::list::ListType; use super::DynContextMatcher; use super::RefContext; use crate::error::Res; use crate::parser::OrgSource; use crate::types::Object; // type Matcher = // dyn for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>>; // #[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 branch_from(trunk: &Rc>>) -> Self { // ContextTree { // tree: List::branch_from(trunk), // } // } // #[allow(dead_code)] // 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 iter(&self) -> impl Iterator>>> { // self.tree.iter() // } // #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] // pub fn check_exit_matcher( // &'r self, // i: OrgSource<'s>, // ) -> IResult, OrgSource<'s>, CustomError>> { // // 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 mut current_class_filter = ExitClass::Gamma; // for current_node in self.iter() { // let context_element = current_node.get_data(); // match context_element { // ContextElement::ExitMatcherNode(exit_matcher) => { // if exit_matcher.class as u32 <= current_class_filter as u32 { // current_class_filter = exit_matcher.class; // let local_context = ContextTree::branch_from(current_node); // let local_result = (exit_matcher.exit_matcher)(&local_context, 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".into(), // )))); // } // /// Indicates if elements should consume the whitespace after them. // /// // /// Defaults to true. // pub fn should_consume_trailing_whitespace(&self) -> bool { // self._should_consume_trailing_whitespace().unwrap_or(true) // } // fn _should_consume_trailing_whitespace(&self) -> Option { // for current_node in self.iter() { // let context_element = current_node.get_data(); // match context_element { // ContextElement::ConsumeTrailingWhitespace(should) => { // return Some(*should); // } // _ => {} // } // } // None // } // } #[derive(Debug)] pub enum ContextElement<'r, 's> { /// Stores a parser that indicates that children should exit upon matching an exit matcher. ExitMatcherNode(ExitMatcherNode<'r>), /// Stores the name of the current element to prevent directly nesting elements of the same type. Context(&'r str), /// Indicates if elements should consume the whitespace after them. ConsumeTrailingWhitespace(bool), /// The contents of a radio target. /// /// If any are found, this will force a 2nd parse through the /// org-mode document since text needs to be re-parsed to look for /// radio links matching the contents of radio targets. RadioTarget(Vec<&'r Vec>>), } pub struct ExitMatcherNode<'r> { // TODO: Should this be "&'r DynContextMatcher<'c>" ? pub exit_matcher: &'r DynContextMatcher<'r>, pub class: ExitClass, } impl<'r> std::fmt::Debug for ExitMatcherNode<'r> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ExitMatcherNode"); formatter.field("class", &self.class.to_string()); formatter.finish() } } #[derive(Debug)] pub struct Context<'r, 's> { global_settings: &'s GlobalSettings<'s>, tree: &'r List<'r, ContextElement<'r, 's>>, } impl<'r, 's> Context<'r, 's> { pub fn new( global_settings: &'s GlobalSettings<'s>, tree: &'r List<'r, ContextElement<'r, 's>>, ) -> Self { Self { global_settings, tree, } } pub fn with_additional_node(&self, data: ContextElement<'r, 's>) -> Self { let new_tree = self.tree.push(data); Self { global_settings: self.global_settings, tree: new_tree, } } pub fn iter(&self) -> super::list::Iter<'r, ContextElement<'r, 's>> { self.tree.iter() } pub fn iter_context(&self) -> Iter<'r, 's> { Iter { next: self.tree.iter_list(), global_settings: self.global_settings, } } pub fn get_parent(&self) -> Option { self.tree.get_parent().map(|parent_tree| Self { global_settings: self.global_settings, tree: parent_tree, }) } } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn document_end<'r, 's>( _context: RefContext<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { eof(input) } pub struct Iter<'r, 's> { global_settings: &'s GlobalSettings<'s>, next: super::list::IterList<'r, ContextElement<'r, 's>>, } impl<'r, 's> Iterator for Iter<'r, 's> { type Item = Context<'r, 's>; fn next(&mut self) -> Option { let next_tree = self.next.next(); let ret = next_tree.map(|parent_tree| Context::new(self.global_settings, parent_tree)); ret } } impl<'r, 's> ContextElement<'r, 's> { pub fn document_context() -> Self { Self::ExitMatcherNode(ExitMatcherNode { exit_matcher: &document_end, class: ExitClass::Document, }) } }