use std::collections::BTreeMap; use nom::combinator::all_consuming; use nom::multi::many0; use super::object_parser::standard_set_object; use crate::context::parser_with_context; use crate::context::Context; use crate::context::ContextElement; use crate::context::GlobalSettings; use crate::context::List; use crate::types::AffiliatedKeywordValue; use crate::types::AffiliatedKeywords; use crate::types::Keyword; pub(crate) fn parse_affiliated_keywords<'g, 's>( global_settings: &'g GlobalSettings<'g, 's>, input: Vec>, ) -> AffiliatedKeywords<'s> { let mut ret = BTreeMap::new(); for kw in input.into_iter() { let translated_name = translate_name(global_settings, kw.key); if is_single_string_keyword(global_settings, translated_name.as_str()) { ret.insert( translated_name, AffiliatedKeywordValue::SingleString(kw.value), ); } else if is_list_of_objects_keyword(global_settings, translated_name.as_str()) { let initial_context = ContextElement::document_context(); let initial_context = Context::new(global_settings, List::new(&initial_context)); // TODO: This should be omitting footnote references let (_remaining, objects) = all_consuming(many0(parser_with_context!( standard_set_object )(&initial_context)))(kw.value.into()) .expect("Object parser should always succeed."); let list_of_lists = ret.entry(translated_name).or_insert_with(|| { AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1)) }); match list_of_lists { AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => { list_of_lists.push(objects); } _ => panic!("Invalid AffiliatedKeywordValue type."), } } else { let list_of_strings = ret .entry(translated_name) .or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1))); match list_of_strings { AffiliatedKeywordValue::ListOfStrings(list_of_strings) => { list_of_strings.push(kw.value); } _ => panic!("Invalid AffiliatedKeywordValue type."), } } } AffiliatedKeywords { keywords: ret } } fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String { for (src, dst) in global_settings.element_keyword_translation_alist { if name.eq_ignore_ascii_case(src) { return dst.to_lowercase(); } } name.to_lowercase() } fn is_single_string_keyword<'g, 's>( _global_settings: &'g GlobalSettings<'g, 's>, name: &'s str, ) -> bool { // TODO: Is this defined by an elisp variable? I'm only seeing this done for plot. name.eq_ignore_ascii_case("plot") } fn is_list_of_objects_keyword<'g, 's>( global_settings: &'g GlobalSettings<'g, 's>, name: &'s str, ) -> bool { for parsed_keyword in global_settings.element_parsed_keywords { if name.eq_ignore_ascii_case(parsed_keyword) { return true; } } false }