use crate::identity::{GeneratedUnit, Identity, OwnerFact, Profile};
use core::marker::PhantomData;
#[path = "type_guard.rs"]
mod guard;
pub trait Kind: 'static {
const NAME: &'static str;
type Content: CanonicalContent;
type Role: Role;
type Question: Question;
}
pub trait CanonicalContent: Clone + Eq + core::fmt::Debug {
fn encode_content_into(&self, into: &mut Vec<u8>);
#[must_use]
fn canonical_content_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
self.encode_content_into(&mut bytes);
bytes
}
}
pub trait Role: Copy + Eq + core::fmt::Debug + 'static {
const ALL: &'static [Self];
#[must_use]
fn name(self) -> &'static str;
#[must_use]
fn destination(self) -> Destination;
#[must_use]
fn slot(self) -> u16 {
slot_in(Self::ALL, self)
}
}
pub trait Question: Copy + Eq + core::fmt::Debug + 'static {
const ALL: &'static [Self];
type Answer: Answer<Question = Self>;
#[must_use]
fn name(self) -> &'static str;
#[must_use]
fn slot(self) -> u16 {
slot_in(Self::ALL, self)
}
}
pub trait Answer: Clone + Eq + core::fmt::Debug {
type Question: Question;
#[must_use]
fn question(&self) -> Self::Question;
fn encode_into(&self, into: &mut Vec<u8>);
#[must_use]
fn human(&self) -> String;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NoQuestions {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SoleRole {
Sole,
}
crate::roster! {
pub enum Destination {
DeclarationSite = "declaration-site",
TestCarrier = "test-carrier",
BenchCarrier = "bench-carrier",
PublicationArtifact = "publication-artifact",
}
}
#[must_use = "a disposition is what happened to a kind, and silence is not a variant"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Disposition {
Generated {
unit: Identity<GeneratedUnit>,
},
NotApplicable {
because: OwnerFact,
},
NotRequested {
because: OwnerFact,
},
UnavailableUnderProfile {
profile: Profile,
because: OwnerFact,
},
}
pub trait DispositionRecord: Clone + Eq + core::fmt::Debug {
fn into_dispositions(self) -> impl Iterator<Item = (&'static str, Disposition)>;
}
pub trait KindSet {
type Dispositions: DispositionRecord;
const NAMES: &'static [&'static str];
}
#[must_use = "a complete disposition set is the witness an accounted expansion requires"]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DispositionSet<Set: KindSet> {
dispositions: Vec<Disposition>,
kind_set: PhantomData<fn() -> Set>,
}
#[must_use = "a disposition-set refusal names the count or kind-name disagreement"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DispositionSetError {
CountMismatch {
expected: usize,
observed: usize,
},
KindMismatch {
expected: &'static str,
observed: &'static str,
},
}
fn slot_in<T: Copy + Eq>(roster: &[T], row: T) -> u16 {
let position = roster
.iter()
.position(|other| *other == row)
.unwrap_or(roster.len());
u16::try_from(position).unwrap_or(u16::MAX)
}
#[macro_export]
macro_rules! roster {
(
$(#[$note:meta])*
$vis:vis enum $name:ident {
$( $(#[$row:meta])* $variant:ident = $declared:literal ),+ $(,)?
}
) => {
$(#[$note])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
$vis enum $name {
$( $(#[$row])* $variant, )+
}
impl $name {
$vis const ALL: &'static [Self] = &[$( Self::$variant ),+];
#[must_use]
$vis const fn name(self) -> &'static str {
match self {
$( Self::$variant => $declared, )+
}
}
}
};
}
#[macro_export]
macro_rules! kinds {
(
set = $set:ident;
dispositions = $record:ident;
$(
$(#[$note:meta])*
$kind:ident = $declared:literal, $seat:ident => $content:ty, $role:ty, $question:ty
);+ $(;)?
) => {
$(
$(#[$note])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct $kind;
impl $crate::kind::Kind for $kind {
const NAME: &'static str = $declared;
type Content = $content;
type Role = $role;
type Question = $question;
}
)+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum $set {
$( $(#[$note])* $kind ),+
}
impl $set {
pub const ALL: &'static [Self] = &[$( Self::$kind ),+];
#[must_use]
pub const fn name(self) -> &'static str {
match self {
$( Self::$kind => <$kind as $crate::kind::Kind>::NAME ),+
}
}
}
impl $crate::kind::KindSet for $set {
type Dispositions = $record;
const NAMES: &'static [&'static str] =
&[$( <$kind as $crate::kind::Kind>::NAME ),+];
}
#[must_use = "a disposition record is what happened to every kind of the set"]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct $record {
$(
#[doc = concat!("What happened to the `", $declared, "` kind.")]
pub $seat: $crate::kind::Disposition
),+
}
impl $record {
#[must_use]
pub const fn under(&self, row: $set) -> &$crate::kind::Disposition {
match row {
$( $set::$kind => &self.$seat ),+
}
}
}
impl $crate::kind::DispositionRecord for $record {
fn into_dispositions(
self,
) -> impl Iterator<Item = (&'static str, $crate::kind::Disposition)> {
[$( (<$kind as $crate::kind::Kind>::NAME, self.$seat) ),+].into_iter()
}
}
};
}