use std::{
cell::RefCell,
fmt::Display,
hash::Hash,
rc::Rc,
};
use hashbrown::{HashMap, HashSet};
use termtree::Tree;
use crate::{column_info_mapping::ColumnInfoMapping, esedb_cache::{CDataTable}};
use anyhow::{bail, Result};
pub(crate) struct ObjectTreeEntry {
name: String,
id: i32,
children: RefCell<HashSet<Rc<ObjectTreeEntry>>>,
}
impl Eq for ObjectTreeEntry {}
impl PartialEq for ObjectTreeEntry {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.id == other.id
}
}
impl Hash for ObjectTreeEntry {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.id.hash(state);
}
}
impl Display for ObjectTreeEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} (id={})", self.name, self.id)
}
}
impl ObjectTreeEntry {
pub(crate) fn from(
data_table: &CDataTable,
mapping: &ColumnInfoMapping,
) -> Result<Rc<ObjectTreeEntry>> {
Self::populate_object_tree(data_table, mapping)
}
pub(crate) fn to_tree(me: &Rc<ObjectTreeEntry>, max_depth: u8) -> Tree<Rc<ObjectTreeEntry>> {
let tree = Tree::new(Rc::clone(me));
if max_depth > 0 {
let leaves: Vec<Tree<Rc<ObjectTreeEntry>>> = me
.children
.borrow()
.iter()
.map(|c| Self::to_tree(c, max_depth - 1))
.collect();
tree.with_leaves(leaves)
} else {
tree
}
}
fn populate_object_tree(
data_table: &CDataTable,
mapping: &ColumnInfoMapping,
) -> Result<Rc<ObjectTreeEntry>> {
log::info!("populating the object tree");
let mut uplinks = HashMap::new();
let mut names = HashMap::new();
for record in data_table.iter_records() {
let id = match record.ds_record_id(mapping)? {
Some(v) => v,
None => bail!("missing record id"),
};
let parent_id = match record.ds_parent_record_id(mapping)? {
Some(v) => v,
None => bail!("missing parent record id"),
};
let name = match record.ds_object_name2(mapping)? {
Some(v) => v,
None => bail!("missing name"),
};
names.insert(id, name);
uplinks.insert(id, parent_id);
}
log::debug!("found {} entries in the DIT", names.len());
log::debug!("ordering those elements in a tree structure now");
Self::create_tree_node(0, &uplinks, &mut names)
}
fn create_tree_node(
object_id: i32,
uplinks: &HashMap<i32, i32>,
names: &mut HashMap<i32, String>,
) -> Result<Rc<ObjectTreeEntry>> {
let name = if object_id == 0 {
String::new()
} else {
names
.remove(&object_id)
.unwrap_or_else(|| panic!("missing name for object with id '{object_id}'"))
};
let my_object = Rc::new(ObjectTreeEntry {
name,
id: object_id,
children: RefCell::new(HashSet::new()),
});
for (child_id, _) in uplinks.iter().filter(|(_, parent)| **parent == object_id) {
let child =
Self::create_tree_node(*child_id, uplinks, names)?;
my_object.children.borrow_mut().insert(child);
}
Ok(my_object)
}
}