use std::{cmp::Ordering, collections::HashMap}; use bstr::BStr; use crate::{ file::{self, SectionBodyIdsLut, SectionId}, lookup, parse::section, File, }; /// Private helper functions impl<'event> File<'event> { /// Adds a new section to the config file, returning the section id of the newly added section. pub(crate) fn push_section_internal(&mut self, mut section: file::Section<'event>) -> SectionId { let new_section_id = SectionId(self.section_id_counter); section.id = new_section_id; self.sections.insert(new_section_id, section); let header = &self.sections[&new_section_id].header; let lookup = self.section_lookup_tree.entry(header.name.clone()).or_default(); let mut found_node = false; if let Some(subsection_name) = header.subsection_name.clone() { for node in lookup.iter_mut() { if let SectionBodyIdsLut::NonTerminal(subsections) = node { found_node = true; subsections .entry(subsection_name.clone()) .or_default() .push(new_section_id); break; } } if !found_node { let mut map = HashMap::new(); map.insert(subsection_name, vec![new_section_id]); lookup.push(SectionBodyIdsLut::NonTerminal(map)); } } else { for node in lookup.iter_mut() { if let SectionBodyIdsLut::Terminal(vec) = node { found_node = true; vec.push(new_section_id); break; } } if !found_node { lookup.push(SectionBodyIdsLut::Terminal(vec![new_section_id])); } } self.section_order.push_back(new_section_id); self.section_id_counter += 1; new_section_id } /// Inserts `section` after the section that comes `before` it, and maintains correct ordering in all of our lookup structures. pub(crate) fn insert_section_after(&mut self, mut section: file::Section<'event>, before: SectionId) -> SectionId { let lookup_section_order = { let section_order = &self.section_order; move |section_id| { section_order .iter() .enumerate() .find_map(|(idx, id)| (*id == section_id).then_some(idx)) .expect("before-section exists") } }; let before_order = lookup_section_order(before); let new_section_id = SectionId(self.section_id_counter); section.id = new_section_id; self.sections.insert(new_section_id, section); let header = &self.sections[&new_section_id].header; let lookup = self.section_lookup_tree.entry(header.name.clone()).or_default(); let mut found_node = false; if let Some(subsection_name) = header.subsection_name.clone() { for node in lookup.iter_mut() { if let SectionBodyIdsLut::NonTerminal(subsections) = node { found_node = true; let sections_with_name_and_subsection_name = subsections.entry(subsection_name.clone()).or_default(); let insert_pos = find_insert_pos_by_order( sections_with_name_and_subsection_name, before_order, lookup_section_order, ); sections_with_name_and_subsection_name.insert(insert_pos, new_section_id); break; } } if !found_node { let mut map = HashMap::new(); map.insert(subsection_name, vec![new_section_id]); lookup.push(SectionBodyIdsLut::NonTerminal(map)); } } else { for node in lookup.iter_mut() { if let SectionBodyIdsLut::Terminal(sections_with_name) = node { found_node = true; let insert_pos = find_insert_pos_by_order(sections_with_name, before_order, lookup_section_order); sections_with_name.insert(insert_pos, new_section_id); break; } } if !found_node { lookup.push(SectionBodyIdsLut::Terminal(vec![new_section_id])); } } self.section_order.insert(before_order + 1, new_section_id); self.section_id_counter += 1; new_section_id } /// Returns the mapping between section and subsection name to section ids. pub(crate) fn section_ids_by_name_and_subname<'a>( &'a self, section_name: &'a str, subsection_name: Option<&BStr>, ) -> Result + ExactSizeIterator + DoubleEndedIterator + '_, lookup::existing::Error> { let section_name = section::Name::from_str_unchecked(section_name); let section_ids = self .section_lookup_tree .get(§ion_name) .ok_or(lookup::existing::Error::SectionMissing)?; let mut maybe_ids = None; if let Some(subsection_name) = subsection_name { for node in section_ids { if let SectionBodyIdsLut::NonTerminal(subsection_lookup) = node { maybe_ids = subsection_lookup.get(subsection_name).map(|v| v.iter().copied()); break; } } } else { for node in section_ids { if let SectionBodyIdsLut::Terminal(subsection_lookup) = node { maybe_ids = Some(subsection_lookup.iter().copied()); break; } } } maybe_ids.ok_or(lookup::existing::Error::SubSectionMissing) } pub(crate) fn section_ids_by_name<'a>( &'a self, section_name: &'a str, ) -> Result + '_, lookup::existing::Error> { let section_name = section::Name::from_str_unchecked(section_name); match self.section_lookup_tree.get(§ion_name) { Some(lookup) => { let mut lut = Vec::with_capacity(self.section_order.len()); for node in lookup { match node { SectionBodyIdsLut::Terminal(v) => lut.extend(v.iter().copied()), SectionBodyIdsLut::NonTerminal(v) => lut.extend(v.values().flatten().copied()), } } Ok(self.section_order.iter().filter(move |a| lut.contains(a)).copied()) } None => Err(lookup::existing::Error::SectionMissing), } } } fn find_insert_pos_by_order( sections_with_name: &[SectionId], before_order: usize, lookup_section_order: impl Fn(SectionId) -> usize, ) -> usize { let mut insert_pos = sections_with_name.len(); // push back by default for (idx, candidate_id) in sections_with_name.iter().enumerate() { let candidate_order = lookup_section_order(*candidate_id); match candidate_order.cmp(&before_order) { Ordering::Less => {} Ordering::Equal => { insert_pos = idx + 1; // insert right after this one break; } Ordering::Greater => { insert_pos = idx; // insert before this one break; } } } insert_pos }