use std::collections::HashSet;
use crate::content::node::NodeState;
use crate::index::{IndexError, IndexResult, strict_boolean};
use crate::segment::record::RecordIdentifier;
pub const APPROXIMATE_COUNT_PREFIX: &str = ":count_";
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct MirrorEntry {
pub key: String,
pub path: String,
}
impl MirrorEntry {
#[must_use]
pub fn content_path(&self) -> &str {
if self.path.is_empty() {
"/"
} else {
&self.path
}
}
}
pub struct MirrorIndex<'provider> {
entries_node: NodeState<'provider>,
definition_path: String,
child_name: String,
}
impl<'provider> MirrorIndex<'provider> {
pub fn open(
definition: &NodeState<'provider>,
definition_path: &str,
child_name: &str,
) -> IndexResult<Option<Self>> {
Ok(definition.child_node(child_name)?.map(|entries_node| Self {
entries_node,
definition_path: definition_path.to_owned(),
child_name: child_name.to_owned(),
}))
}
#[must_use]
pub fn child_name(&self) -> &str {
&self.child_name
}
pub fn for_each_entry(
&self,
mut visit: impl FnMut(&MirrorEntry) -> IndexResult<()>,
) -> IndexResult<()> {
for (key, key_node) in sorted_children(&self.entries_node)? {
let mut records_on_path = HashSet::new();
self.walk_key(&key, &key_node, "", &mut records_on_path, &mut visit)?;
}
Ok(())
}
pub fn entries(&self) -> IndexResult<Vec<MirrorEntry>> {
let mut entries = Vec::new();
self.for_each_entry(|entry| {
entries.push(entry.clone());
Ok(())
})?;
Ok(entries)
}
pub fn count_for_key(&self, key: &str) -> IndexResult<u64> {
let Some(key_node) = self.entries_node.child_node(key)? else {
return Ok(0);
};
let mut count = 0u64;
let mut records_on_path = HashSet::new();
self.walk_key(key, &key_node, "", &mut records_on_path, &mut |_| {
count += 1;
Ok(())
})?;
Ok(count)
}
pub fn keys(&self) -> IndexResult<Vec<String>> {
Ok(self
.entries_node
.child_node_entries()?
.into_iter()
.map(|(name, _)| name)
.collect())
}
pub fn approximate_counter_count(&self) -> IndexResult<usize> {
let mut count = approximate_counters(&self.entries_node)?.len();
for (_, key_node) in self.entries_node.child_node_entries()? {
count += approximate_counters(&key_node)?.len();
}
Ok(count)
}
fn walk_key(
&self,
key: &str,
key_node: &NodeState<'provider>,
path: &str,
records_on_path: &mut HashSet<RecordIdentifier>,
visit: &mut impl FnMut(&MirrorEntry) -> IndexResult<()>,
) -> IndexResult<()> {
let record = key_node.record_identifier();
if !records_on_path.insert(record) {
return Err(IndexError::Record(crate::Error::InvalidFormat {
details: format!(
"the index storage of {} at {}/{key} contains node record {record} in its \
own subtree; the node records form a cycle",
self.definition_path, self.child_name
),
}));
}
if strict_boolean(key_node.property("match")?.as_ref()) {
visit(&MirrorEntry {
key: key.to_owned(),
path: path.to_owned(),
})?;
}
for (name, child) in sorted_children(key_node)? {
if name.starts_with(':') {
continue;
}
self.walk_key(
key,
&child,
&format!("{path}/{name}"),
records_on_path,
visit,
)?;
}
records_on_path.remove(&record);
Ok(())
}
}
fn sorted_children<'provider>(
node: &NodeState<'provider>,
) -> IndexResult<Vec<(String, NodeState<'provider>)>> {
let mut children = node.child_node_entries()?;
children.sort_by(|first, second| first.0.as_bytes().cmp(second.0.as_bytes()));
Ok(children)
}
pub fn approximate_counters(node: &NodeState<'_>) -> IndexResult<Vec<String>> {
let mut names: Vec<String> = node
.properties()?
.into_iter()
.map(|property| property.name)
.filter(|name| name.starts_with(APPROXIMATE_COUNT_PREFIX))
.collect();
names.sort();
Ok(names)
}