use petgraph::visit::NodeIndexable;
pub use daggy::petgraph;
use daggy::petgraph::visit::Topo;
use daggy::Dag;
use daggy::NodeIndex;
pub use daggy::Walker;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Display;
use uvm_live_platform::{Download, Release, Module};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InstallStatus {
Unknown,
Missing,
Installed,
}
impl Default for InstallStatus {
fn default() -> InstallStatus {
InstallStatus::Unknown
}
}
impl fmt::Display for InstallStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InstallStatus::Missing => write!(f, "missing"),
InstallStatus::Installed => write!(f, "installed"),
_ => write!(f, "unknown"),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum UnityComponent<'a> {
Editor(&'a Download),
Module(&'a Module)
}
impl Display for UnityComponent<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UnityComponent::Editor(_) => {
write!(f, "Editor")
}
UnityComponent::Module(module) => {
write!(f, "{} - {}", module.id(), module.description())
}
}
}
}
type DagNode<'a> = (UnityComponent<'a>, InstallStatus);
type DagEdge = ();
type ModulesDag<'a> = Dag<DagNode<'a>, DagEdge>;
#[derive(Debug)]
pub struct InstallGraph<'a> {
release: &'a Release,
dag: ModulesDag<'a>,
}
impl<'a> From<&'a Release> for InstallGraph<'a> {
fn from(release: &'a Release) -> Self {
Self::from(release)
}
}
impl<'a> InstallGraph<'a> {
pub fn node_count(&self) -> usize {
self.dag.node_count()
}
pub fn keep(&mut self, modules: &HashSet<String>) {
self.dag = self.dag.filter_map(
|_n, (c, install_status)| {
match c {
UnityComponent::Editor(_) if modules.contains("Unity") => Some((*c, *install_status)),
UnityComponent::Module(module) if modules.contains(module.id()) => Some((*c, *install_status)),
_ => None,
}
},
|_, _| Some(()),
);
}
pub fn from(release: &'a Release) -> Self {
let dag = Dag::new();
let mut graph = InstallGraph {
release,
dag
};
graph.setup_graph();
graph
}
fn setup_graph(&mut self) {
let release = &self.release;
for download in &release.downloads {
let d = self
.dag
.add_node((UnityComponent::Editor(&download), InstallStatus::default()));
for m in &download.modules {
Self::setup_graph_modules(&mut self.dag, d, m);
}
}
}
fn setup_graph_modules<'b>(graph: &mut ModulesDag<'b>, parent: NodeIndex, module: &'b Module) {
let module_node = graph.add_child(parent, (), (UnityComponent::Module(module), InstallStatus::default()));
for sub_module in module.sub_modules() {
Self::setup_graph_modules(graph, module_node.1, sub_module);
}
}
pub fn topo(&self) -> Topo<NodeIndex, fixedbitset::FixedBitSet> {
Topo::new(&self.dag)
}
pub fn version(&self) -> &String {
&self.release.version
}
pub fn release(&self) -> &Release {
&self.release
}
pub fn mark_all(&mut self, install_status: InstallStatus) {
self.dag = self
.dag
.map(|_, (c, _)| (*c, install_status), |_, e| (*e));
}
pub fn mark_all_missing(&mut self) {
self.mark_all(InstallStatus::Missing)
}
pub fn mark_installed(&mut self, components: &HashSet<String>) {
self.dag = self.dag.filter_map(
|_n, (c, _)| {
match c {
UnityComponent::Editor(_) if components.contains("Unity") => Some((*c, InstallStatus::Installed)),
UnityComponent::Module(module) if components.contains(module.id()) => Some((*c, InstallStatus::Installed)),
_ => Some((*c, InstallStatus::Missing)),
}
},
|_, _| Some(()),
);
}
pub fn component(&self, node: NodeIndex) -> Option<UnityComponent<'_>> {
self.dag.node_weight(node).map(|(component, _)| *component)
}
pub fn install_status(&self, node: NodeIndex) -> Option<&InstallStatus> {
self.dag.node_weight(node).map(|(_, status)| status)
}
pub fn get_node_id(&self, component: &str) -> Option<NodeIndex> {
self.dag.raw_nodes().iter().enumerate().find(|(_, node)| {
let (c, _) = &node.weight;
match c {
UnityComponent::Editor(_) if component == "Unity" => true,
UnityComponent::Module(module) => module.id() == component,
_ => false,
}
}).map(|(index,_)| self.dag.from_index(index))
}
pub fn depth(&self, node: NodeIndex) -> usize {
self.dag
.recursive_walk(node, |g, n| g.parents(n).walk_next(g))
.iter(&self.dag)
.count()
}
pub fn get_dependend_modules(&self, node: NodeIndex) -> Vec<(DagNode<'_>, NodeIndex)> {
self.dag
.recursive_walk(node, |g, n| g.parents(n).walk_next(g))
.iter(&self.dag)
.map(|(_, n)| (*self.dag.node_weight(n).unwrap(), n))
.collect()
}
pub fn get_sub_modules(&self, node: NodeIndex) -> Vec<(&DagNode<'_>, NodeIndex)> {
let mut modules = Vec::new();
for (_, n) in self.dag.children(node).iter(&self.dag) {
modules.push((self.dag.node_weight(n).unwrap(), n));
modules.append(&mut self.get_sub_modules(n));
}
modules
}
pub fn context(&self) -> &ModulesDag<'a> {
&self.dag
}
}