diff options
Diffstat (limited to 'vendor/gix-config/src/file/util.rs')
-rw-r--r-- | vendor/gix-config/src/file/util.rs | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/vendor/gix-config/src/file/util.rs b/vendor/gix-config/src/file/util.rs new file mode 100644 index 000000000..5c60f1fd5 --- /dev/null +++ b/vendor/gix-config/src/file/util.rs @@ -0,0 +1,190 @@ +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<impl Iterator<Item = SectionId> + 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<impl Iterator<Item = SectionId> + '_, 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 +} |