use super::approximate_counter::ApproximateCounter;
use crate::PropertyType;
use crate::error::{Error, Result};
use crate::index::property::unique::ENTRY_PROPERTY_NAME;
use crate::segment::record::RecordIdentifier;
use crate::writer::record_writer::{
ChildNodesToWrite, PropertyToWrite, PropertyValuesToWrite, RecordWriter, SegmentSink,
};
const MATCH_PROPERTY_NAME: &str = "match";
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct BuilderAccounting {
pub nodes_written: u64,
pub peak_resident_children: usize,
}
struct OpenLevel {
name: String,
children: Vec<(String, RecordIdentifier)>,
matched: bool,
counter: ApproximateCounter,
}
pub struct MirrorBuilder<'writer, Sink: SegmentSink> {
writer: &'writer mut RecordWriter<Sink>,
root_children: Vec<(String, RecordIdentifier)>,
levels: Vec<OpenLevel>,
counter: ApproximateCounter,
accounting: BuilderAccounting,
}
impl<'writer, Sink: SegmentSink> MirrorBuilder<'writer, Sink> {
pub fn new(writer: &'writer mut RecordWriter<Sink>) -> Self {
Self {
writer,
root_children: Vec::new(),
levels: Vec::new(),
counter: ApproximateCounter::default(),
accounting: BuilderAccounting::default(),
}
}
pub fn push(&mut self, key: &str, path: &str) -> Result<()> {
let elements: Vec<&str> = path.split('/').filter(|part| !part.is_empty()).collect();
self.counter.record_one_insert();
if self.levels.first().map(|level| level.name.as_str()) != Some(key) {
self.close_to_depth(0)?;
self.levels.push(OpenLevel {
name: key.to_owned(),
children: Vec::new(),
matched: false,
counter: ApproximateCounter::default(),
});
}
let shared = self.shared_prefix(&elements);
self.close_to_depth(1 + shared)?;
for element in &elements[shared..] {
self.levels.push(OpenLevel {
name: (*element).to_owned(),
children: Vec::new(),
matched: false,
counter: ApproximateCounter::default(),
});
}
if let Some(addressed) = self.levels.last_mut() {
addressed.matched = true;
}
if let Some(key_level) = self.levels.first_mut() {
key_level.counter.record_one_insert();
}
Ok(())
}
pub fn finish(mut self) -> Result<(RecordIdentifier, BuilderAccounting)> {
self.close_to_depth(0)?;
let children = std::mem::take(&mut self.root_children);
let properties = self.counter.properties(self.writer)?;
let record = self
.writer
.write_node(None, &[], &child_nodes(&children), &properties)?;
self.accounting.nodes_written += 1;
Ok((record, self.accounting))
}
fn shared_prefix(&self, elements: &[&str]) -> usize {
let mut shared = 0;
while shared < elements.len() {
let Some(level) = self.levels.get(1 + shared) else {
break;
};
if level.name != elements[shared] {
break;
}
shared += 1;
}
shared
}
fn close_to_depth(&mut self, depth: usize) -> Result<()> {
while self.levels.len() > depth {
let Some(level) = self.levels.pop() else {
break;
};
let record = self.write_level(&level)?;
let parent = match self.levels.last_mut() {
Some(parent) => &mut parent.children,
None => &mut self.root_children,
};
parent.push((level.name, record));
self.record_residency();
}
Ok(())
}
fn write_level(&mut self, level: &OpenLevel) -> Result<RecordIdentifier> {
let mut properties = if level.matched {
let value = self.writer.write_string("true")?;
vec![PropertyToWrite {
name: MATCH_PROPERTY_NAME.to_owned(),
property_type: PropertyType::Boolean,
values: PropertyValuesToWrite::Single(value),
}]
} else {
Vec::new()
};
properties.extend(level.counter.properties(self.writer)?);
let record = self
.writer
.write_node(None, &[], &child_nodes(&level.children), &properties)?;
self.accounting.nodes_written += 1;
Ok(record)
}
fn record_residency(&mut self) {
let resident: usize = self.root_children.len()
+ self
.levels
.iter()
.map(|level| level.children.len())
.sum::<usize>();
self.accounting.peak_resident_children =
self.accounting.peak_resident_children.max(resident);
}
}
pub struct UniqueBuilder<'writer, Sink: SegmentSink> {
writer: &'writer mut RecordWriter<Sink>,
children: Vec<(String, RecordIdentifier)>,
open: Option<(String, Vec<String>)>,
counter: ApproximateCounter,
accounting: BuilderAccounting,
}
impl<'writer, Sink: SegmentSink> UniqueBuilder<'writer, Sink> {
pub fn new(writer: &'writer mut RecordWriter<Sink>) -> Self {
Self {
writer,
children: Vec::new(),
open: None,
counter: ApproximateCounter::default(),
accounting: BuilderAccounting::default(),
}
}
pub fn push(&mut self, key: &str, path: &str) -> Result<()> {
self.counter.record_one_insert();
match &mut self.open {
Some((open_key, paths)) if open_key == key => {
if paths.iter().all(|existing| existing != path) {
let mut found = paths.clone();
found.push(path.to_owned());
return Err(Error::DuplicateUniqueKey {
key: key.to_owned(),
paths: found,
});
}
}
_ => {
self.close_open()?;
self.open = Some((key.to_owned(), vec![path.to_owned()]));
}
}
self.accounting.peak_resident_children = self
.accounting
.peak_resident_children
.max(self.children.len());
Ok(())
}
pub fn finish(mut self) -> Result<(RecordIdentifier, BuilderAccounting)> {
self.close_open()?;
let properties = self.counter.properties(self.writer)?;
let record =
self.writer
.write_node(None, &[], &child_nodes(&self.children), &properties)?;
self.accounting.nodes_written += 1;
Ok((record, self.accounting))
}
fn close_open(&mut self) -> Result<()> {
let Some((key, paths)) = self.open.take() else {
return Ok(());
};
let values: Vec<RecordIdentifier> = paths
.iter()
.map(|path| self.writer.write_string(path))
.collect::<Result<Vec<_>>>()?;
let record = self.writer.write_node(
None,
&[],
&ChildNodesToWrite::Zero,
&[PropertyToWrite {
name: ENTRY_PROPERTY_NAME.to_owned(),
property_type: PropertyType::String,
values: PropertyValuesToWrite::Multiple(values),
}],
)?;
self.accounting.nodes_written += 1;
self.children.push((key, record));
Ok(())
}
}
fn child_nodes(children: &[(String, RecordIdentifier)]) -> ChildNodesToWrite {
match children {
[] => ChildNodesToWrite::Zero,
[(name, node)] => ChildNodesToWrite::One {
name: name.clone(),
node: *node,
},
many => ChildNodesToWrite::Many(many.to_vec()),
}
}