blob: 78bc4736f63d0538f41e4bc7df1f25dcad0d361c [file] [log] [blame]
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 ExactSizeIterator<Item = SectionId> + DoubleEndedIterator + '_, lookup::existing::Error> {
let section_name = section::Name::from_str_unchecked(section_name);
let section_ids = self
.section_lookup_tree
.get(&section_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(&section_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
}