use getset::Getters;
use hashbrown::{HashMap, HashSet};
use crate::cache::RecordPointer;
use crate::cache::{self, RecordId};
use crate::ntds::link_table_builder::LinkTableBuilder;
use crate::win32_types::Rdn;
use crate::{Membership, MembershipSet, SerializationType};
use super::DataTable;
pub struct LinkTable {
pub(crate) forward_map: HashMap<RecordId, HashSet<RecordPointer>>,
pub(crate) backward_map: HashMap<RecordId, HashSet<RecordPointer>>,
}
impl LinkTable {
pub fn new<'info, 'db>(
link_table: cache::LinkTable<'info, 'db>,
data_table: &cache::DataTable<'info, 'db>,
schema_record_id: RecordPointer,
) -> crate::ntds::Result<Self> {
log::info!("reading link information and creating link_table cache");
let builder = LinkTableBuilder::from(link_table, data_table, schema_record_id)?;
builder.build(data_table.metadata())
}
pub fn member_of(&self, dnt: &RecordId) -> Option<&HashSet<RecordPointer>> {
self.backward_map.get(dnt)
}
pub fn members(&self, dnt: &RecordId) -> Option<&HashSet<RecordPointer>> {
self.forward_map.get(dnt)
}
pub fn member_names_of(&self, object_id: RecordId, data_table: &DataTable<'_, '_>) -> Vec<Rdn> {
let member_of = if let Some(children) = self.member_of(&object_id) {
children
.iter()
.map(|child_id| &data_table.data_table().metadata()[child_id])
.map(|record| record.rdn().clone())
.collect()
} else {
vec![]
};
member_of
}
pub fn member_refs_of<T: SerializationType>(
&self,
object_id: RecordId,
data_table: &DataTable<'_, '_>,
) -> MembershipSet<T> {
let member_of = if let Some(children) = self.member_of(&object_id) {
children
.iter()
.map(|child_id| &data_table.data_table().metadata()[child_id])
.map(|record| {
(
*record.record_ptr(),
record.rdn().clone(),
record.sid().clone(),
record.sam_account_name().clone(),
)
})
.collect()
} else {
vec![]
};
MembershipSet::<T>::from(member_of.into_iter().map(Membership::from))
}
pub fn transitive_member_of(&self, dnt: &RecordId) -> TransitiveClosure<RecordId> {
Self::transitive_closure(dnt, &self.backward_map)
}
pub fn transitive_members(&self, dnt: &RecordId) -> TransitiveClosure<RecordId> {
Self::transitive_closure(dnt, &self.forward_map)
}
fn transitive_closure(
record_id: &RecordId,
mapping_table: &HashMap<RecordId, HashSet<RecordPointer>>,
) -> TransitiveClosure<RecordId> {
TransitiveClosure::<RecordId> {
value: *record_id,
children: if let Some(children) = mapping_table.get(record_id) {
children
.iter()
.map(|ch| *(ch.ds_record_id()))
.map(|child_record_id| {
Self::transitive_closure(&child_record_id, mapping_table)
})
.collect()
} else {
Vec::new()
},
}
}
}
#[derive(Getters)]
#[getset(get = "pub")]
pub struct TransitiveClosure<T> {
value: T,
children: Vec<Self>,
}
impl<S> TransitiveClosure<S> {
pub fn filter_map<D, F>(self, mapping: F) -> Option<TransitiveClosure<D>>
where
F: Fn(S) -> Option<D> + Clone,
{
match mapping(self.value) {
None => None,
Some(value) => Some(TransitiveClosure::<D> {
value,
children: self
.children
.into_iter()
.filter_map(|ch| ch.filter_map(mapping.clone()))
.collect(),
}),
}
}
pub fn explode(self) -> (S, Vec<Self>) {
(self.value, self.children)
}
}