use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet};
use crate::types::Provenance;
pub type Provenanced<T> = (Provenance, T);
pub type Contributor = (Provenance, String);
pub type Contributors = Vec<Contributor>;
pub type Collected<A> = Vec<Provenanced<A>>;
pub type MessagePair<T> = (T, String);
pub type ItemAssertion<Item> = MessagePair<Item>;
pub type ItemAssertionInput<Item> = Provenanced<ItemAssertion<Item>>;
pub type ItemInput<Item> = Provenanced<ItemRequirements<Item>>;
pub type ItemAssertionGroups<Item> =
BTreeMap<<Item as FileItemRequirement>::Identity, Vec<ItemAssertionInput<Item>>>;
pub type RequiredItemResolution<Item> = ResolvedRequirement<Item, ItemAssertion<Item>>;
pub type ForbiddenItemResolution<Item> = ResolvedRequirement<Item, String>;
pub type ItemRequirementMap<Item> =
BTreeMap<<Item as FileItemRequirement>::Identity, RequiredItemResolution<Item>>;
pub type ForbiddenItemMap<Item> =
BTreeMap<<Item as FileItemRequirement>::Identity, ForbiddenItemResolution<Item>>;
pub type GlobAssertion<Glob> = MessagePair<Glob>;
pub type GlobAssertionInput<Glob> = Provenanced<GlobAssertion<Glob>>;
pub type GlobInput<Glob> = Provenanced<ForbiddenGlobRequirements<Glob>>;
pub type GlobAssertionGroups<Glob> =
BTreeMap<<Glob as ForbiddenGlobRequirement>::Identity, Vec<GlobAssertionInput<Glob>>>;
pub type GlobResolutionMap<Glob> =
BTreeMap<<Glob as ForbiddenGlobRequirement>::Identity, ResolvedRequirement<Glob, String>>;
pub type ListInput = Provenanced<ListRequirements>;
pub type ListExact = MessagePair<Vec<String>>;
pub type ExactInput = Provenanced<ListExact>;
pub type ResolvedExactList = ResolvedRequirement<Vec<String>, ListExact>;
pub type ResolvedStringMembers = BTreeMap<String, ResolvedRequirement<(), String>>;
pub type MemberInputs = BTreeMap<String, Contributors>;
pub type ExactItems<Item> = MessagePair<Vec<Item>>;
pub type ExactItemsInput<Item> = Provenanced<ExactItems<Item>>;
pub type MapInput<K, A> = Provenanced<BTreeMap<K, A>>;
pub type MapInputs<K, A> = Vec<MapInput<K, A>>;
pub type GroupedAssertions<K, A> = BTreeMap<K, Vec<Provenanced<A>>>;
pub type ResolvedMap<K, A> = BTreeMap<K, ResolvedRequirement<<A as Resolve>::Merged, A>>;
pub type ResolvedAssertion<A> = ResolvedRequirement<<A as Resolve>::Merged, A>;
pub type ResolvedAssertionOption<A> = Option<ResolvedAssertion<A>>;
pub type ResolvedSame<T> = ResolvedRequirement<T, T>;
pub type ResolvedSameOption<T> = Option<ResolvedSame<T>>;
pub type OptionalInput<T> = Provenanced<Option<T>>;
pub type VersionFloor = MessagePair<String>;
pub type KeyedValueMap<S, M> = BTreeMap<String, (S, M)>;
#[derive(Debug, Clone)]
pub enum ScalarAssertion<T> {
Equals(T, String),
AtLeast(T, String),
AtMost(T, String),
Range(T, T, String),
OneOf(BTreeSet<T>, String),
Present(String),
Absent(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ScalarOperation {
Equals,
AtLeast,
AtMost,
Range,
OneOf,
Present,
Absent,
}
pub trait ScalarValue: Clone + Eq + Ord {
fn render(&self) -> String;
fn compare_for_order(&self, other: &Self) -> Option<Ordering>;
}
impl<T> ScalarAssertion<T> {
#[must_use]
pub const fn operation(&self) -> ScalarOperation {
match self {
Self::Equals(..) => ScalarOperation::Equals,
Self::AtLeast(..) => ScalarOperation::AtLeast,
Self::AtMost(..) => ScalarOperation::AtMost,
Self::Range(..) => ScalarOperation::Range,
Self::OneOf(..) => ScalarOperation::OneOf,
Self::Present(_) => ScalarOperation::Present,
Self::Absent(_) => ScalarOperation::Absent,
}
}
#[must_use]
pub fn message(&self) -> &str {
match self {
Self::Equals(_, msg)
| Self::AtLeast(_, msg)
| Self::AtMost(_, msg)
| Self::Range(_, _, msg)
| Self::OneOf(_, msg)
| Self::Present(msg)
| Self::Absent(msg) => msg,
}
}
}
impl<T: PartialEq> PartialEq for ScalarAssertion<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Equals(a, _), Self::Equals(b, _))
| (Self::AtLeast(a, _), Self::AtLeast(b, _))
| (Self::AtMost(a, _), Self::AtMost(b, _)) => a == b,
(Self::Range(a_min, a_max, _), Self::Range(b_min, b_max, _)) => {
a_min == b_min && a_max == b_max
}
(Self::OneOf(a, _), Self::OneOf(b, _)) => a == b,
(Self::Present(_), Self::Present(_)) | (Self::Absent(_), Self::Absent(_)) => true,
_ => false,
}
}
}
impl<T: Eq> Eq for ScalarAssertion<T> {}
#[derive(Debug, Clone)]
pub struct ConflictEntry {
pub key: String,
pub reason: String,
pub contributors: Contributors,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedRequirement<Merged, A> {
pub merged: Merged,
pub collected: Collected<A>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ItemRequirements<Item> {
pub required: Vec<ItemAssertion<Item>>,
pub forbidden: Vec<ItemAssertion<Item>>,
pub exact: Option<ExactItems<Item>>,
}
impl<Item> Default for ItemRequirements<Item> {
fn default() -> Self {
Self {
required: Vec::new(),
forbidden: Vec::new(),
exact: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedExactItems<Item>
where
Item: FileItemRequirement,
{
pub identities: BTreeSet<Item::Identity>,
pub items: ItemRequirementMap<Item>,
pub collected: Vec<ExactItemsInput<Item>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedItemRequirements<Item>
where
Item: FileItemRequirement,
{
pub required: ItemRequirementMap<Item>,
pub forbidden: ForbiddenItemMap<Item>,
pub exact: Option<ResolvedExactItems<Item>>,
}
impl<Item> Default for ResolvedItemRequirements<Item>
where
Item: FileItemRequirement,
{
fn default() -> Self {
Self {
required: BTreeMap::new(),
forbidden: BTreeMap::new(),
exact: None,
}
}
}
pub trait FileItemRequirement: Sized + Clone {
type Identity: Ord + Clone + std::fmt::Debug;
fn merge_identity(&self) -> Self::Identity;
fn compose_item(
key: &str,
items: Vec<ItemAssertionInput<Self>>,
conflicts: &mut Vec<ConflictEntry>,
) -> Option<RequiredItemResolution<Self>>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForbiddenGlobRequirements<Glob> {
pub globs: Vec<GlobAssertion<Glob>>,
}
impl<Glob> Default for ForbiddenGlobRequirements<Glob> {
fn default() -> Self {
Self { globs: Vec::new() }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedForbiddenGlobRequirements<Glob>
where
Glob: ForbiddenGlobRequirement,
{
pub globs: GlobResolutionMap<Glob>,
}
impl<Glob> Default for ResolvedForbiddenGlobRequirements<Glob>
where
Glob: ForbiddenGlobRequirement,
{
fn default() -> Self {
Self {
globs: BTreeMap::new(),
}
}
}
pub trait ForbiddenGlobRequirement: Sized + Clone {
type Identity: Ord + Clone + std::fmt::Debug;
fn merge_identity(&self) -> Self::Identity;
fn render(&self) -> String;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyedItem<Value> {
pub file_key: String,
pub value: Value,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ListRequirements {
pub contains: BTreeMap<String, String>,
pub excludes: BTreeMap<String, String>,
pub exact: Option<ListExact>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ResolvedListRequirements {
pub contains: ResolvedStringMembers,
pub excludes: ResolvedStringMembers,
pub exact: Option<ResolvedExactList>,
}
pub trait Resolve: Sized + Clone {
type Merged: Clone;
fn resolve(
key: &str,
items: Vec<Provenanced<Self>>,
conflicts: &mut Vec<ConflictEntry>,
) -> ResolvedAssertionOption<Self>;
}