use crate::graph::CodeGraph;
use crate::id::{GraphNodeId, index_of, position};
use crate::node::GraphNodeKind;
use super::error::GraphAnalysisError;
use super::index::SelectedIndexes;
pub struct DeclaredModulePartition {
owners: Box<[GraphNodeId]>,
roots: Box<[GraphNodeId]>,
members: Box<[Box<[GraphNodeId]>]>,
}
impl DeclaredModulePartition {
pub fn owner(&self, node: GraphNodeId) -> Option<GraphNodeId> {
self.owners.get(index_of(node.index())).copied()
}
pub fn roots(&self) -> &[GraphNodeId] {
&self.roots
}
pub(crate) fn owners(&self) -> &[GraphNodeId] {
&self.owners
}
pub fn members(&self, root: GraphNodeId) -> Option<&[GraphNodeId]> {
self.roots
.binary_search(&root)
.ok()
.and_then(|at| self.members.get(at))
.map(|held| &**held)
}
}
struct Ownership {
containers: Box<[bool]>,
owners: Box<[Option<GraphNodeId>]>,
chain: Vec<GraphNodeId>,
}
impl Ownership {
fn new(graph: &CodeGraph) -> Self {
let containers: Box<[bool]> = graph
.nodes()
.iter()
.map(|node| matches!(node.kind(), GraphNodeKind::Container { .. }))
.collect();
let owners = vec![None; containers.len()].into_boxed_slice();
Self {
containers,
owners,
chain: Vec::new(),
}
}
fn settled(&self, node: GraphNodeId) -> Option<GraphNodeId> {
self.owners.get(index_of(node.index())).copied().flatten()
}
fn is_container(&self, node: GraphNodeId) -> bool {
self.containers
.get(index_of(node.index()))
.copied()
.unwrap_or(false)
}
fn push(&mut self, node: GraphNodeId) {
self.chain.push(node);
}
fn settle(&mut self, owner: GraphNodeId) {
for node in self.chain.drain(..) {
if let Some(slot) = self.owners.get_mut(index_of(node.index())) {
*slot = Some(owner);
}
}
}
fn abandon(&mut self) {
self.chain.clear();
}
fn width(&self) -> usize {
self.owners.len()
}
fn finish(self) -> Result<DeclaredModulePartition, GraphAnalysisError> {
let unsettled = self.owners.iter().position(Option::is_none);
match unsettled {
Some(at) => Err(GraphAnalysisError::ConsumedGraphInvariant {
node: GraphNodeId::new(position(at)),
}),
None => Ok(assembled(
self.owners.iter().flatten().copied().collect(),
&self.containers,
)),
}
}
}
pub(crate) fn derive(
graph: &CodeGraph,
indexes: &SelectedIndexes,
) -> Result<DeclaredModulePartition, GraphAnalysisError> {
let mut ownership = Ownership::new(graph);
for node in graph.nodes() {
let owner = nearest_container(&mut ownership, indexes, node.id());
match owner {
Some(found) => ownership.settle(found),
None => ownership.abandon(),
}
}
ownership.finish()
}
fn nearest_container(
ownership: &mut Ownership,
indexes: &SelectedIndexes,
start: GraphNodeId,
) -> Option<GraphNodeId> {
let mut current = Some(start);
for _ in 0..ownership.width() {
let node = current?;
let settled = ownership.settled(node);
match settled {
Some(owner) => return Some(owner),
None => ownership.push(node),
}
match ownership.is_container(node) {
true => return Some(node),
false => current = indexes.parent(node),
}
}
None
}
fn assembled(owners: Box<[GraphNodeId]>, containers: &[bool]) -> DeclaredModulePartition {
let roots: Box<[GraphNodeId]> = containers
.iter()
.enumerate()
.filter(|(_, container)| **container)
.map(|(at, _)| GraphNodeId::new(position(at)))
.collect();
let members = grouped(&owners, &roots);
DeclaredModulePartition {
owners,
roots,
members,
}
}
fn grouped(owners: &[GraphNodeId], roots: &[GraphNodeId]) -> Box<[Box<[GraphNodeId]>]> {
let mut buckets: Vec<Vec<GraphNodeId>> = vec![Vec::new(); roots.len()];
for (at, owner) in owners.iter().enumerate() {
record(&mut buckets, roots, *owner, GraphNodeId::new(position(at)));
}
buckets
.into_iter()
.map(Vec::into_boxed_slice)
.collect::<Vec<Box<[GraphNodeId]>>>()
.into_boxed_slice()
}
fn record(
buckets: &mut [Vec<GraphNodeId>],
roots: &[GraphNodeId],
owner: GraphNodeId,
node: GraphNodeId,
) {
let bucket = roots
.binary_search(&owner)
.ok()
.and_then(|at| buckets.get_mut(at));
if let Some(held) = bucket {
held.push(node);
}
}