callisto-graph 0.3.1

Dependency graph, cascade, aggregation, and config resolution for callisto.
Documentation
use std::collections::BTreeMap;

use crate::config::groups::{GroupDef, GroupMember, GroupMemberKind, GroupTable};
use crate::error::GraphError;
use crate::napi::NapiTargetsIndex;
use crate::resolver::DependencyResolver;
use crate::tags::TagIndex;
use callisto_format::Versioning;
use callisto_model::{Diagnostic, GroupName, PackageId, Severity, Version};

#[derive(Clone, Debug, Default)]
pub struct GroupCheckOutcome {
    pub new_members: BTreeMap<GroupName, Vec<PackageId>>,
    pub diagnostics: Vec<Diagnostic>,
}

pub fn pre_mutation_checks<D: DependencyResolver>(
    _graph: &D,
    groups: &GroupTable,
    base: &BTreeMap<PackageId, Version>,
    tags: &TagIndex,
    _napi: &NapiTargetsIndex,
) -> Result<GroupCheckOutcome, GraphError> {
    let mut outcome = GroupCheckOutcome::default();

    for g in groups.fixed.values() {
        let released: Vec<PackageId> = g
            .members(GroupMemberKind::Package)
            .filter_map(|m| match m {
                GroupMember::Package(ref id) => {
                    if tags.last_tag(id).is_some() {
                        Some(id.clone())
                    } else {
                        None
                    }
                }
                _ => None,
            })
            .collect();

        let fresh: Vec<PackageId> = g
            .members(GroupMemberKind::Package)
            .filter_map(|m| match m {
                GroupMember::Package(ref id) => {
                    if tags.last_tag(id).is_none() {
                        Some(id.clone())
                    } else {
                        None
                    }
                }
                _ => None,
            })
            .collect();

        let pairs: Vec<(PackageId, Version)> = released
            .iter()
            .filter_map(|id| base.get(id).map(|v| (id.clone(), v.clone())))
            .collect();

        if pairs.len() > 1 {
            let first_v = &pairs[0].1;
            let mut divergent = false;
            for (_id, v) in &pairs[1..] {
                if v.grammar() != first_v.grammar() {
                    return Err(GraphError::GroupGrammarMismatch {
                        group: g.name.clone(),
                        members: pairs,
                    });
                }
                if Version::compare(v, first_v).ok() != Some(std::cmp::Ordering::Equal) {
                    divergent = true;
                }
            }
            if divergent {
                return Err(GraphError::FixedGroupDivergent {
                    group: g.name.clone(),
                    members: pairs,
                });
            }
        }

        if !fresh.is_empty() {
            outcome.new_members.insert(g.name.clone(), fresh);
        }
    }

    Ok(outcome)
}

pub fn fixed_group_target(
    g: &GroupDef,
    base: &BTreeMap<PackageId, Version>,
    severities: &BTreeMap<PackageId, Severity>,
    tags: &TagIndex,
    _pre: Option<&callisto_format::PreState>,
) -> Result<Version, GraphError> {
    let released: Vec<PackageId> = g
        .members(GroupMemberKind::Package)
        .filter_map(|m| match m {
            GroupMember::Package(ref id) => {
                if tags.last_tag(id).is_some() {
                    Some(id.clone())
                } else {
                    None
                }
            }
            _ => None,
        })
        .collect();

    let mut max_sev = Severity::None;
    for m in g.members(GroupMemberKind::Package) {
        if let GroupMember::Package(ref id) = m {
            if let Some(&s) = severities.get(id) {
                if s > max_sev {
                    max_sev = s;
                }
            }
        }
    }

    let aligned_base = if !released.is_empty() {
        base.get(&released[0])
            .cloned()
            .unwrap_or_else(|| Version::semver(1, 0, 0))
    } else {
        Version::semver(0, 0, 0)
    };

    let versioning = callisto_format::SemVerVersioning;

    versioning
        .bump(&aligned_base, max_sev)
        .map_err(GraphError::Bump)
}