chug-cli 0.1.3

The fastest way to consume Homebrew bottles
Documentation
use std::collections::{BTreeMap, BTreeSet};

use ptree::{TreeBuilder, print_tree};

use crate::db::models::{Dependency, DownloadedBottle};

pub fn list_bottles() -> anyhow::Result<()> {
    let bottles = DownloadedBottle::get_all()?;

    for bottle in bottles {
        println!("{} {}", bottle.name(), bottle.version());
    }

    Ok(())
}

pub fn display_tree() -> anyhow::Result<()> {
    let bottles = DownloadedBottle::get_all()?;
    let dependencies = Dependency::get_all()?;

    let bottle_map = bottles
        .into_iter()
        .map(|b| (b.id(), b))
        .collect::<BTreeMap<_, _>>();
    let mut dependency_map = BTreeMap::new();
    for dependency in dependencies {
        dependency_map
            .entry(dependency.dependent_id())
            .or_insert_with(Vec::new)
            .push(dependency.dependency_id());
    }

    let get_dependencies = |id: Option<i32>| {
        let mut v = dependency_map
            .get(&id)
            .map(Vec::as_slice)
            .unwrap_or_default()
            .iter()
            .map(|id| &bottle_map[id])
            .collect::<Vec<_>>();
        v.sort_by_key(|b| b.name());
        v
    };

    let mut builder = TreeBuilder::new("Installed bottles:".to_owned());
    let mut stack = vec![get_dependencies(None).into_iter()];
    let mut processed = BTreeSet::new();
    while let Some(children) = stack.last_mut() {
        if let Some(child) = children.next() {
            if processed.insert(child.id()) {
                builder.begin_child(format!("{} {}", child.name(), child.version()));
                stack.push(get_dependencies(Some(child.id())).into_iter());
            } else {
                builder.add_empty_child(format!("{} {} (*)", child.name(), child.version()));
            }
        } else {
            stack.pop();
            if !stack.is_empty() {
                builder.end_child();
            }
        }
    }

    let tree = builder.build();
    print_tree(&tree)?;

    Ok(())
}