#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResolveError {
None,
Many,
}
pub fn resolve<V>(candidates: impl IntoIterator<Item = Option<V>>) -> Result<V, ResolveError> {
let mut found: Option<V> = None;
for candidate in candidates {
if candidate.is_some() {
if found.is_some() {
return Err(ResolveError::Many);
}
found = candidate;
}
}
found.ok_or(ResolveError::None)
}
pub trait TaggedUnionError: Sized {
fn empty(kinds: &'static str) -> Self;
fn ambiguous() -> Self;
}
pub fn resolve_or_err<V, E: TaggedUnionError>(
candidates: impl IntoIterator<Item = Option<V>>,
kinds: &'static str,
) -> Result<V, E> {
resolve(candidates).map_err(|e| match e {
ResolveError::None => E::empty(kinds),
ResolveError::Many => E::ambiguous(),
})
}
#[macro_export]
macro_rules! declare_tagged_union_error {
(
$(#[$attr:meta])*
$vis:vis $name:ident,
empty = $empty:literal,
ambiguous = $ambiguous:literal $(,)?
) => {
$(#[$attr])*
#[derive(
::std::clone::Clone,
::std::marker::Copy,
::std::fmt::Debug,
::thiserror::Error,
::std::cmp::PartialEq,
::std::cmp::Eq,
)]
$vis enum $name {
#[error($empty)]
Empty(&'static str),
#[error($ambiguous)]
Ambiguous,
}
impl $crate::tagged_union::TaggedUnionError for $name {
fn empty(kinds: &'static str) -> Self {
Self::Empty(kinds)
}
fn ambiguous() -> Self {
Self::Ambiguous
}
}
};
}
#[macro_export]
macro_rules! declare_tagged_union_impls {
(
parent = $parent:ty,
kind = $kind:ty,
variant = $variant:ident,
error = $err:ty,
kind_list = $kind_list:expr $(,)?
) => {
impl $parent {
pub fn variant(&self) -> ::std::result::Result<$variant<'_>, $err> {
<Self as $crate::tagged_union::TaggedUnion>::variant(self)
}
}
impl $crate::tagged_union::VariantSelector<$parent> for $kind {
type Variant<'a> = $variant<'a>;
fn select<'a>(self, parent: &'a $parent) -> ::std::option::Option<$variant<'a>>
where
Self: 'a,
{
<$kind>::select(self, parent)
}
}
impl $crate::tagged_union::TaggedUnion for $parent {
type Kind = $kind;
type Error = $err;
const KIND_LIST: &'static str = $kind_list;
}
};
}
pub trait VariantSelector<P: ?Sized>: Copy + 'static {
type Variant<'a>: VariantKind<Self>
where
P: 'a,
Self: 'a;
fn select<'a>(self, parent: &'a P) -> Option<Self::Variant<'a>>
where
Self: 'a;
}
pub trait VariantKind<K: Copy + 'static> {
fn variant_kind(&self) -> K;
}
#[track_caller]
pub fn assert_variant_round_trip<T, F>(make_parent: F)
where
T: TaggedUnion,
T::Kind: PartialEq + std::fmt::Debug,
F: Fn(T::Kind) -> T,
{
for k in <T::Kind as tatara_closed_set::ClosedSet>::ALL
.iter()
.copied()
{
let parent = make_parent(k);
let selected = k.select(&parent).unwrap_or_else(|| {
panic!("VariantSelector::select must return Some for populated slot {k:?}")
});
assert_eq!(
<<T::Kind as VariantSelector<T>>::Variant<'_> as VariantKind<T::Kind>>::variant_kind(
&selected,
),
k,
"select→variant_kind round-trip failed for {k:?}",
);
let resolved = parent.variant().ok().unwrap_or_else(|| {
panic!("TaggedUnion::variant must resolve exactly-one populated for {k:?}")
});
assert_eq!(
<<T::Kind as VariantSelector<T>>::Variant<'_> as VariantKind<T::Kind>>::variant_kind(
&resolved,
),
k,
"variant()→variant_kind resolver disagreed on {k:?}",
);
}
}
pub trait TaggedUnion: Sized {
type Kind: tatara_closed_set::ClosedSet + VariantSelector<Self>;
type Error: TaggedUnionError;
const KIND_LIST: &'static str;
fn variant(&self) -> Result<<Self::Kind as VariantSelector<Self>>::Variant<'_>, Self::Error> {
resolve_or_err(
<Self::Kind as tatara_closed_set::ClosedSet>::ALL
.iter()
.copied()
.map(|k| k.select(self)),
Self::KIND_LIST,
)
}
}
#[track_caller]
pub fn assert_kind_list_matches_closed_set<T: TaggedUnion>() {
let derived = <T::Kind as tatara_closed_set::ClosedSet>::labels_joined("/");
assert_eq!(
derived,
T::KIND_LIST,
"TaggedUnion KIND_LIST drift — must equal <T::Kind as ClosedSet>::labels_joined(\"/\")",
);
}
#[track_caller]
pub fn assert_two_slots_ambiguous<T, F>(two_slot: F)
where
T: TaggedUnion,
T::Kind: PartialEq + std::fmt::Debug,
T::Error: PartialEq + std::fmt::Debug,
F: Fn(T::Kind, T::Kind) -> T,
{
let expected = T::Error::ambiguous();
for a in <T::Kind as tatara_closed_set::ClosedSet>::ALL
.iter()
.copied()
{
for b in <T::Kind as tatara_closed_set::ClosedSet>::ALL
.iter()
.copied()
{
if a == b {
continue;
}
let parent = two_slot(a, b);
let err = parent.variant().err().unwrap_or_else(|| {
panic!("({a:?}, {b:?}) two-slot parent must not resolve to a variant")
});
assert_eq!(err, expected, "({a:?}, {b:?}) should resolve Ambiguous");
}
}
}
#[track_caller]
pub fn assert_single_slot_key_matches_label<T, F>(single_slot: F)
where
T: TaggedUnion + serde::Serialize,
T::Kind: PartialEq + std::fmt::Debug,
F: Fn(T::Kind) -> T,
{
assert_wire_key_matches_label::<T, T::Kind, F>(single_slot);
}
#[track_caller]
pub fn assert_wire_key_matches_label<T, K, F>(single_slot: F)
where
T: serde::Serialize,
K: tatara_closed_set::ClosedSet + PartialEq + std::fmt::Debug,
F: Fn(K) -> T,
{
for k in <K as tatara_closed_set::ClosedSet>::ALL.iter().copied() {
let parent = single_slot(k);
let value = serde_json::to_value(&parent)
.unwrap_or_else(|e| panic!("single_slot({k:?}) must serialize as JSON: {e}"));
let obj = value.as_object().unwrap_or_else(|| {
panic!("single_slot({k:?}) must serialize to a JSON object, got {value}")
});
let keys: Vec<&String> = obj.keys().collect();
assert_eq!(
keys.len(),
1,
"single_slot({k:?}) must serialize to exactly one populated field, got keys: {keys:?}",
);
let expected = <K as tatara_closed_set::ClosedSet>::label(k);
assert_eq!(
keys[0].as_str(),
expected,
"wire-key drift for {k:?}: single_slot's populated field '{}' must equal <K as ClosedSet>::label ({expected:?})",
keys[0],
);
}
}
#[track_caller]
pub fn assert_display_matches_label<T>()
where
T: tatara_closed_set::ClosedSet + core::fmt::Display + PartialEq + core::fmt::Debug,
{
let type_name = core::any::type_name::<T>();
for &v in <T as tatara_closed_set::ClosedSet>::ALL {
let rendered = v.to_string();
let expected = <T as tatara_closed_set::ClosedSet>::label(v);
assert_eq!(
rendered.as_str(),
expected,
"{type_name}: Display drifted from ClosedSet::label for {v:?} — expected {expected:?}, got {rendered:?}",
);
}
}
#[track_caller]
pub fn assert_label_matches_serde_serialization<T>()
where
T: tatara_closed_set::ClosedSet + serde::Serialize + core::fmt::Debug,
{
let type_name = core::any::type_name::<T>();
for &v in <T as tatara_closed_set::ClosedSet>::ALL {
let serialized = serde_json::to_string(&v).unwrap_or_else(|e| {
panic!("{type_name}: closed-set variant {v:?} must serialize: {e}")
});
let unquoted = serialized.trim_start_matches('"').trim_end_matches('"');
let expected = <T as tatara_closed_set::ClosedSet>::label(v);
assert_eq!(
unquoted,
expected,
"{type_name}: serde output drifted from ClosedSet::label for {v:?} — expected {expected:?}, got {unquoted:?} (full serialization {serialized:?})",
);
}
}
#[track_caller]
pub fn assert_closed_set_convention_panel<T>()
where
T: tatara_closed_set::ClosedSet
+ serde::Serialize
+ core::fmt::Display
+ PartialEq
+ core::fmt::Debug,
T::Unknown: core::fmt::Display,
{
tatara_closed_set::assert_closed_set_well_formed::<T>();
assert_display_matches_label::<T>();
assert_label_matches_serde_serialization::<T>();
}
#[track_caller]
pub fn assert_tagged_union_convention_panel<T, F1, F2>(single_slot: F1, two_slot: F2)
where
T: TaggedUnion + serde::Serialize,
T::Kind: PartialEq + std::fmt::Debug,
T::Error: PartialEq + std::fmt::Debug,
F1: Fn(T::Kind) -> T,
F2: Fn(T::Kind, T::Kind) -> T,
{
assert_kind_list_matches_closed_set::<T>();
assert_variant_round_trip::<T, _>(&single_slot);
assert_two_slots_ambiguous::<T, _>(two_slot);
assert_single_slot_key_matches_label::<T, _>(single_slot);
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum V {
A,
B,
C,
}
#[test]
fn empty_candidate_list_is_none() {
let r: Result<V, _> = resolve(std::iter::empty());
assert_eq!(r.unwrap_err(), ResolveError::None);
}
#[test]
fn all_none_is_none() {
let r: Result<V, _> = resolve([None, None, None]);
assert_eq!(r.unwrap_err(), ResolveError::None);
}
#[test]
fn single_some_is_resolved_regardless_of_position() {
assert_eq!(resolve([Some(V::A), None, None]).unwrap(), V::A);
assert_eq!(resolve([None, Some(V::B), None]).unwrap(), V::B);
assert_eq!(resolve([None, None, Some(V::C)]).unwrap(), V::C);
}
#[test]
fn two_or_more_some_is_many() {
assert_eq!(
resolve([Some(V::A), Some(V::B), None]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([Some(V::A), None, Some(V::C)]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([None, Some(V::B), Some(V::C)]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([Some(V::A), Some(V::B), Some(V::C)]).unwrap_err(),
ResolveError::Many
);
}
#[test]
fn many_short_circuits_after_second_some() {
let mut visited = 0usize;
let candidates = (0..4).map(|i| {
visited += 1;
Some(i)
});
let _ = resolve(candidates);
assert_eq!(visited, 2);
}
#[test]
fn works_with_borrowed_enum_view() {
#[derive(Debug, PartialEq)]
enum View<'a> {
X(&'a u32),
Y(&'a String),
}
let x = 7u32;
let r = resolve([Some(View::X(&x)), None]).unwrap();
assert_eq!(r, View::X(&7));
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum E {
Empty(&'static str),
Ambiguous,
}
impl TaggedUnionError for E {
fn empty(kinds: &'static str) -> Self {
E::Empty(kinds)
}
fn ambiguous() -> Self {
E::Ambiguous
}
}
#[test]
fn resolve_or_err_dispatches_each_arm_through_the_trait() {
const KINDS: &str = "a/b/c";
assert_eq!(
resolve_or_err::<V, E>([Some(V::A), None, None], KINDS).unwrap(),
V::A
);
assert_eq!(
resolve_or_err::<V, E>([None, Some(V::B), None], KINDS).unwrap(),
V::B
);
assert_eq!(
resolve_or_err::<V, E>([None, None, None], KINDS).unwrap_err(),
E::Empty(KINDS)
);
assert_eq!(
resolve_or_err::<V, E>([Some(V::A), Some(V::B), None], KINDS).unwrap_err(),
E::Ambiguous
);
}
#[test]
fn resolve_or_err_empty_carries_the_caller_kinds_verbatim() {
const KINDS_ALPHA: &str = "alpha/beta";
const KINDS_GAMMA: &str = "gamma/delta/epsilon";
assert_eq!(
resolve_or_err::<V, E>([None, None], KINDS_ALPHA).unwrap_err(),
E::Empty(KINDS_ALPHA)
);
assert_eq!(
resolve_or_err::<V, E>([None, None, None], KINDS_GAMMA).unwrap_err(),
E::Empty(KINDS_GAMMA)
);
}
#[test]
fn resolve_or_err_short_circuits_on_many() {
let mut visited = 0usize;
let candidates = (0..4).map(|i| {
visited += 1;
Some(i)
});
let _ = resolve_or_err::<i32, E>(candidates, "irrelevant");
assert_eq!(visited, 2);
}
crate::declare_tagged_union_error! {
pub(super) MacroEmittedError,
empty = "test carrier has no variant set (one of {0} required)",
ambiguous = "test carrier has multiple variants set; exactly one required",
}
#[test]
fn macro_emitted_carrier_projects_through_resolve_or_err() {
const KINDS: &str = "one/two/three";
assert_eq!(
resolve_or_err::<V, MacroEmittedError>([Some(V::A), None, None], KINDS).unwrap(),
V::A
);
assert_eq!(
resolve_or_err::<V, MacroEmittedError>([None, None, None], KINDS).unwrap_err(),
MacroEmittedError::Empty(KINDS)
);
assert_eq!(
resolve_or_err::<V, MacroEmittedError>([Some(V::A), Some(V::B), None], KINDS)
.unwrap_err(),
MacroEmittedError::Ambiguous
);
}
#[test]
fn macro_emitted_carrier_display_renders_caller_literals_verbatim() {
assert_eq!(
MacroEmittedError::Empty("alpha/beta").to_string(),
"test carrier has no variant set (one of alpha/beta required)",
);
assert_eq!(
MacroEmittedError::Ambiguous.to_string(),
"test carrier has multiple variants set; exactly one required",
);
}
#[test]
fn macro_emitted_carrier_is_copy() {
fn assert_copy<T: Copy>() {}
assert_copy::<MacroEmittedError>();
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, tatara_closed_set::DeriveClosedSet)]
#[closed_set(via = "as_str", generate_unknown, display)]
enum LocalKind {
Alpha,
Beta,
Gamma,
}
impl LocalKind {
const ALL: [Self; 3] = [Self::Alpha, Self::Beta, Self::Gamma];
const fn as_str(self) -> &'static str {
match self {
Self::Alpha => "alpha",
Self::Beta => "beta",
Self::Gamma => "gamma",
}
}
}
#[derive(Default, serde::Serialize)]
struct LocalParent {
#[serde(skip_serializing_if = "Option::is_none")]
alpha: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
beta: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
gamma: Option<u32>,
}
#[derive(Debug, PartialEq)]
enum LocalVariant<'a> {
Alpha(&'a u32),
Beta(&'a u32),
Gamma(&'a u32),
}
impl VariantSelector<LocalParent> for LocalKind {
type Variant<'a> = LocalVariant<'a>;
fn select<'a>(self, parent: &'a LocalParent) -> Option<LocalVariant<'a>>
where
Self: 'a,
{
match self {
Self::Alpha => parent.alpha.as_ref().map(LocalVariant::Alpha),
Self::Beta => parent.beta.as_ref().map(LocalVariant::Beta),
Self::Gamma => parent.gamma.as_ref().map(LocalVariant::Gamma),
}
}
}
impl VariantKind<LocalKind> for LocalVariant<'_> {
fn variant_kind(&self) -> LocalKind {
match self {
Self::Alpha(_) => LocalKind::Alpha,
Self::Beta(_) => LocalKind::Beta,
Self::Gamma(_) => LocalKind::Gamma,
}
}
}
crate::declare_tagged_union_error! {
pub(super) LocalParentError,
empty = "local carrier has no variant set (one of {0} required)",
ambiguous = "local carrier has multiple variants set; exactly one required",
}
impl TaggedUnion for LocalParent {
type Kind = LocalKind;
type Error = LocalParentError;
const KIND_LIST: &'static str = "alpha/beta/gamma";
}
#[test]
fn assert_kind_list_matches_closed_set_accepts_coherent_impl() {
assert_kind_list_matches_closed_set::<LocalParent>();
}
#[test]
#[should_panic(expected = "TaggedUnion KIND_LIST drift")]
fn assert_kind_list_matches_closed_set_rejects_drifted_impl() {
struct Drifted;
impl VariantSelector<Drifted> for LocalKind {
type Variant<'a> = LocalVariant<'a>;
fn select<'a>(self, _: &'a Drifted) -> Option<LocalVariant<'a>>
where
Self: 'a,
{
None
}
}
impl TaggedUnion for Drifted {
type Kind = LocalKind;
type Error = LocalParentError;
const KIND_LIST: &'static str = "beta/alpha/gamma";
}
assert_kind_list_matches_closed_set::<Drifted>();
}
#[test]
fn every_production_tagged_union_binds_through_the_testkit_primitive() {
assert_kind_list_matches_closed_set::<crate::intent::Intent>();
assert_kind_list_matches_closed_set::<crate::encapsulates::EncapsulationKind>();
assert_kind_list_matches_closed_set::<crate::export::ArtifactSource>();
assert_kind_list_matches_closed_set::<crate::export::VectorChannel>();
}
#[test]
fn every_production_tagged_union_binds_through_the_wire_key_testkit_primitive() {
assert_single_slot_key_matches_label::<crate::intent::Intent, _>(single_slot_intent_probe);
assert_single_slot_key_matches_label::<crate::encapsulates::EncapsulationKind, _>(
single_slot_encapsulation_kind_probe,
);
assert_single_slot_key_matches_label::<crate::export::ArtifactSource, _>(
single_slot_artifact_source_probe,
);
assert_single_slot_key_matches_label::<crate::export::VectorChannel, _>(
single_slot_vector_channel_probe,
);
}
#[test]
fn assert_tagged_union_convention_panel_accepts_coherent_local_impl() {
fn single_slot(k: LocalKind) -> LocalParent {
match k {
LocalKind::Alpha => LocalParent {
alpha: Some(11),
..Default::default()
},
LocalKind::Beta => LocalParent {
beta: Some(22),
..Default::default()
},
LocalKind::Gamma => LocalParent {
gamma: Some(33),
..Default::default()
},
}
}
fn two_slot(a: LocalKind, b: LocalKind) -> LocalParent {
let mut p = LocalParent::default();
for k in [a, b] {
match k {
LocalKind::Alpha => p.alpha = Some(11),
LocalKind::Beta => p.beta = Some(22),
LocalKind::Gamma => p.gamma = Some(33),
}
}
p
}
assert_tagged_union_convention_panel::<LocalParent, _, _>(single_slot, two_slot);
}
#[test]
fn every_production_tagged_union_binds_through_the_convention_panel_testkit_primitive() {
assert_tagged_union_convention_panel::<crate::intent::Intent, _, _>(
single_slot_intent_probe,
two_slot_intent_probe,
);
assert_tagged_union_convention_panel::<crate::encapsulates::EncapsulationKind, _, _>(
single_slot_encapsulation_kind_probe,
two_slot_encapsulation_kind_probe,
);
assert_tagged_union_convention_panel::<crate::export::ArtifactSource, _, _>(
single_slot_artifact_source_probe,
two_slot_artifact_source_probe,
);
assert_tagged_union_convention_panel::<crate::export::VectorChannel, _, _>(
single_slot_vector_channel_probe,
two_slot_vector_channel_probe,
);
}
#[test]
fn assert_display_matches_label_accepts_coherent_impl() {
assert_display_matches_label::<LocalKind>();
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, tatara_closed_set::DeriveClosedSet)]
#[closed_set(via = "as_str", generate_unknown)]
enum DisplayDriftKind {
Alpha,
Beta,
}
impl DisplayDriftKind {
const ALL: [Self; 2] = [Self::Alpha, Self::Beta];
const fn as_str(self) -> &'static str {
match self {
Self::Alpha => "alpha",
Self::Beta => "beta",
}
}
}
impl std::fmt::Display for DisplayDriftKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}!", self.as_str())
}
}
#[test]
#[should_panic(expected = "Display drifted from ClosedSet::label")]
fn assert_display_matches_label_rejects_drifted_impl() {
assert_display_matches_label::<DisplayDriftKind>();
}
#[test]
fn every_production_display_impl_binds_through_the_testkit_primitive() {
assert_display_matches_label::<crate::allocation::AllocationPhase>();
assert_display_matches_label::<crate::boundary::ConditionKind>();
assert_display_matches_label::<crate::classification::Arity>();
assert_display_matches_label::<crate::classification::CalmClassification>();
assert_display_matches_label::<crate::classification::ConvergencePointType>();
assert_display_matches_label::<crate::classification::DataClassification>();
assert_display_matches_label::<crate::classification::HorizonKind>();
assert_display_matches_label::<crate::classification::OptimizationDirection>();
assert_display_matches_label::<crate::classification::SubstrateType>();
assert_display_matches_label::<crate::compliance::VerificationPhase>();
assert_display_matches_label::<crate::encapsulates::EncapsulationMode>();
assert_display_matches_label::<crate::encapsulates::EncapsulationTarget>();
assert_display_matches_label::<crate::export::ArtifactKind>();
assert_display_matches_label::<crate::export::ChannelKind>();
assert_display_matches_label::<crate::export::ExportTrigger>();
assert_display_matches_label::<crate::export::ReportFormat>();
assert_display_matches_label::<crate::export::ReportPayloadShape>();
assert_display_matches_label::<crate::intent::IntentKind>();
assert_display_matches_label::<crate::intent::WorkloadKind>();
assert_display_matches_label::<crate::lifetime::LifetimeKind>();
assert_display_matches_label::<crate::lifetime::TeardownPolicy>();
assert_display_matches_label::<crate::lifetime_clock::AutoTerminateKind>();
assert_display_matches_label::<crate::lifetime_clock::TerminateReasonKind>();
assert_display_matches_label::<crate::matrix::SelectStrategyKind>();
assert_display_matches_label::<crate::pool::MemberState>();
assert_display_matches_label::<crate::pool::PoolPhase>();
assert_display_matches_label::<crate::pool::ReplacementPolicy>();
assert_display_matches_label::<crate::pool::ReturnPolicy>();
assert_display_matches_label::<crate::signal::SighupStrategy>();
assert_display_matches_label::<crate::spec::MustReachPhase>();
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
serde::Serialize,
tatara_closed_set::DeriveClosedSet,
)]
#[serde(rename_all = "lowercase")]
#[closed_set(via = "as_str", generate_unknown)]
enum SerdeAlignedKind {
Alpha,
Beta,
}
impl SerdeAlignedKind {
const ALL: [Self; 2] = [Self::Alpha, Self::Beta];
const fn as_str(self) -> &'static str {
match self {
Self::Alpha => "alpha",
Self::Beta => "beta",
}
}
}
#[test]
fn assert_label_matches_serde_serialization_accepts_coherent_impl() {
assert_label_matches_serde_serialization::<SerdeAlignedKind>();
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
serde::Serialize,
tatara_closed_set::DeriveClosedSet,
)]
#[serde(rename_all = "UPPERCASE")]
#[closed_set(via = "as_str", generate_unknown)]
enum SerdeDriftKind {
Alpha,
Beta,
}
impl SerdeDriftKind {
const ALL: [Self; 2] = [Self::Alpha, Self::Beta];
const fn as_str(self) -> &'static str {
match self {
Self::Alpha => "alpha",
Self::Beta => "beta",
}
}
}
#[test]
#[should_panic(expected = "serde output drifted from ClosedSet::label")]
fn assert_label_matches_serde_serialization_rejects_drifted_impl() {
assert_label_matches_serde_serialization::<SerdeDriftKind>();
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
serde::Serialize,
tatara_closed_set::DeriveClosedSet,
)]
#[serde(rename_all = "lowercase")]
#[closed_set(via = "as_str", generate_unknown, display)]
enum PanelAlignedKind {
Alpha,
Beta,
}
impl PanelAlignedKind {
const ALL: [Self; 2] = [Self::Alpha, Self::Beta];
const fn as_str(self) -> &'static str {
match self {
Self::Alpha => "alpha",
Self::Beta => "beta",
}
}
}
#[test]
fn assert_closed_set_convention_panel_accepts_coherent_impl() {
assert_closed_set_convention_panel::<PanelAlignedKind>();
}
#[test]
fn every_production_convention_panel_binds_through_the_testkit_primitive() {
assert_closed_set_convention_panel::<crate::allocation::AllocationPhase>();
assert_closed_set_convention_panel::<crate::boundary::ConditionKind>();
assert_closed_set_convention_panel::<crate::classification::CalmClassification>();
assert_closed_set_convention_panel::<crate::classification::ConvergencePointType>();
assert_closed_set_convention_panel::<crate::classification::DataClassification>();
assert_closed_set_convention_panel::<crate::classification::HorizonKind>();
assert_closed_set_convention_panel::<crate::classification::OptimizationDirection>();
assert_closed_set_convention_panel::<crate::classification::SubstrateType>();
assert_closed_set_convention_panel::<crate::compliance::VerificationPhase>();
assert_closed_set_convention_panel::<crate::encapsulates::EncapsulationMode>();
assert_closed_set_convention_panel::<crate::export::ExportTrigger>();
assert_closed_set_convention_panel::<crate::export::ReportFormat>();
assert_closed_set_convention_panel::<crate::intent::WorkloadKind>();
assert_closed_set_convention_panel::<crate::lifetime::TeardownPolicy>();
assert_closed_set_convention_panel::<crate::pool::MemberState>();
assert_closed_set_convention_panel::<crate::pool::PoolPhase>();
assert_closed_set_convention_panel::<crate::pool::ReplacementPolicy>();
assert_closed_set_convention_panel::<crate::pool::ReturnPolicy>();
assert_closed_set_convention_panel::<crate::signal::SighupStrategy>();
assert_closed_set_convention_panel::<crate::spec::MustReachPhase>();
}
#[test]
fn every_production_serde_serialization_binds_through_the_testkit_primitive() {
assert_label_matches_serde_serialization::<crate::allocation::AllocationPhase>();
assert_label_matches_serde_serialization::<crate::boundary::ConditionKind>();
assert_label_matches_serde_serialization::<crate::classification::CalmClassification>();
assert_label_matches_serde_serialization::<crate::classification::ConvergencePointType>();
assert_label_matches_serde_serialization::<crate::classification::DataClassification>();
assert_label_matches_serde_serialization::<crate::classification::HorizonKind>();
assert_label_matches_serde_serialization::<crate::classification::OptimizationDirection>();
assert_label_matches_serde_serialization::<crate::classification::SubstrateType>();
assert_label_matches_serde_serialization::<crate::compliance::VerificationPhase>();
assert_label_matches_serde_serialization::<crate::encapsulates::EncapsulationMode>();
assert_label_matches_serde_serialization::<crate::export::ExportTrigger>();
assert_label_matches_serde_serialization::<crate::export::ReportFormat>();
assert_label_matches_serde_serialization::<crate::intent::WorkloadKind>();
assert_label_matches_serde_serialization::<crate::lifetime::TeardownPolicy>();
assert_label_matches_serde_serialization::<crate::pool::MemberState>();
assert_label_matches_serde_serialization::<crate::pool::PoolPhase>();
assert_label_matches_serde_serialization::<crate::pool::ReplacementPolicy>();
assert_label_matches_serde_serialization::<crate::pool::ReturnPolicy>();
assert_label_matches_serde_serialization::<crate::signal::SighupStrategy>();
assert_label_matches_serde_serialization::<crate::spec::MustReachPhase>();
}
fn single_slot_intent_probe(kind: crate::intent::IntentKind) -> crate::intent::Intent {
use crate::intent::{
AplicacaoIntent, ContainerIntent, FluxIntent, GuestIntent, Intent, IntentKind,
LispIntent, NixIntent, WorkloadKind,
};
match kind {
IntentKind::Nix => Intent {
nix: Some(NixIntent {
flake_ref: "f".into(),
attribute: "a".into(),
system: None,
attic_cache: None,
extra_args: vec![],
delegate_to_nix_build: false,
}),
..Intent::default()
},
IntentKind::Flux => Intent {
flux: Some(FluxIntent {
git_repository: "g".into(),
path: "p".into(),
git_repository_namespace: None,
target_namespace: None,
decrypt_sops: true,
helm_chart: None,
helm_values: None,
}),
..Intent::default()
},
IntentKind::Lisp => Intent {
lisp: Some(LispIntent {
source: "()".into(),
reader: "tatara-lisp".into(),
version: "v1".into(),
bindings: std::collections::BTreeMap::new(),
}),
..Intent::default()
},
IntentKind::Container => Intent {
container: Some(ContainerIntent {
image: "x".into(),
replicas: None,
command: vec![],
args: vec![],
env: std::collections::BTreeMap::new(),
workload_kind: WorkloadKind::default(),
}),
..Intent::default()
},
IntentKind::Aplicacao => Intent {
aplicacao: Some(AplicacaoIntent {
chart_ref: "x".into(),
version: "1".into(),
profile: String::new(),
values_overlay: serde_json::Value::Null,
release_name: None,
target_namespace: None,
install_timeout: None,
}),
..Intent::default()
},
IntentKind::Guest => Intent {
guest: Some(GuestIntent {
spec: serde_json::json!({"name": "x"}),
state_dir: None,
allow_remote_build: None,
}),
..Intent::default()
},
}
}
fn single_slot_encapsulation_kind_probe(
target: crate::encapsulates::EncapsulationTarget,
) -> crate::encapsulates::EncapsulationKind {
use crate::encapsulates::{
BareWorkload, EncapsulationKind, EncapsulationTarget, ExistingHelmRelease,
ExistingKustomization,
};
match target {
EncapsulationTarget::ExistingHelmRelease => EncapsulationKind {
existing_helm_release: Some(ExistingHelmRelease {
namespace: "ns".into(),
name: "hr".into(),
release_name: "rel".into(),
}),
..EncapsulationKind::default()
},
EncapsulationTarget::ExistingKustomization => EncapsulationKind {
existing_kustomization: Some(ExistingKustomization {
namespace: "ns".into(),
name: "ks".into(),
}),
..EncapsulationKind::default()
},
EncapsulationTarget::BareWorkload => {
let mut sel = std::collections::BTreeMap::new();
sel.insert("app".into(), "x".into());
EncapsulationKind {
bare_workload: Some(BareWorkload {
namespace: "ns".into(),
selector: sel,
}),
..EncapsulationKind::default()
}
}
}
}
fn single_slot_artifact_source_probe(
kind: crate::export::ArtifactKind,
) -> crate::export::ArtifactSource {
use crate::export::{
ArtifactKind, ArtifactSource, ProcessSnapshotSource, ReceiptsSource, ReportFormat,
RunMarkerSource, TestReportSource,
};
match kind {
ArtifactKind::Receipts => ArtifactSource {
receipts: Some(ReceiptsSource::default()),
..ArtifactSource::default()
},
ArtifactKind::TestReport => ArtifactSource {
test_report: Some(TestReportSource {
configmap: "cm".into(),
key: "k".into(),
format: ReportFormat::Junit,
namespace: None,
}),
..ArtifactSource::default()
},
ArtifactKind::ProcessSnapshot => ArtifactSource {
process_snapshot: Some(ProcessSnapshotSource::default()),
..ArtifactSource::default()
},
ArtifactKind::RunMarker => ArtifactSource {
run_marker: Some(RunMarkerSource::default()),
..ArtifactSource::default()
},
}
}
fn single_slot_vector_channel_probe(
kind: crate::export::ChannelKind,
) -> crate::export::VectorChannel {
use crate::export::{
ChannelKind, HttpEventChannel, NatsSubjectChannel, StdoutChannel, VectorChannel,
};
match kind {
ChannelKind::HttpEvent => VectorChannel {
http_event: Some(HttpEventChannel {
endpoint: None,
signal_type: "x".into(),
}),
..VectorChannel::default()
},
ChannelKind::NatsSubject => VectorChannel {
nats_subject: Some(NatsSubjectChannel {
subject: "s".into(),
stream: "S".into(),
url: None,
}),
..VectorChannel::default()
},
ChannelKind::Stdout => VectorChannel {
stdout: Some(StdoutChannel::default()),
..VectorChannel::default()
},
}
}
fn two_slot_intent_probe(
a: crate::intent::IntentKind,
b: crate::intent::IntentKind,
) -> crate::intent::Intent {
let ia = single_slot_intent_probe(a);
let ib = single_slot_intent_probe(b);
crate::intent::Intent {
nix: ia.nix.or(ib.nix),
flux: ia.flux.or(ib.flux),
lisp: ia.lisp.or(ib.lisp),
container: ia.container.or(ib.container),
aplicacao: ia.aplicacao.or(ib.aplicacao),
guest: ia.guest.or(ib.guest),
}
}
fn two_slot_encapsulation_kind_probe(
a: crate::encapsulates::EncapsulationTarget,
b: crate::encapsulates::EncapsulationTarget,
) -> crate::encapsulates::EncapsulationKind {
let ka = single_slot_encapsulation_kind_probe(a);
let kb = single_slot_encapsulation_kind_probe(b);
crate::encapsulates::EncapsulationKind {
existing_helm_release: ka.existing_helm_release.or(kb.existing_helm_release),
existing_kustomization: ka.existing_kustomization.or(kb.existing_kustomization),
bare_workload: ka.bare_workload.or(kb.bare_workload),
}
}
fn two_slot_artifact_source_probe(
a: crate::export::ArtifactKind,
b: crate::export::ArtifactKind,
) -> crate::export::ArtifactSource {
let sa = single_slot_artifact_source_probe(a);
let sb = single_slot_artifact_source_probe(b);
crate::export::ArtifactSource {
receipts: sa.receipts.or(sb.receipts),
test_report: sa.test_report.or(sb.test_report),
process_snapshot: sa.process_snapshot.or(sb.process_snapshot),
run_marker: sa.run_marker.or(sb.run_marker),
}
}
fn two_slot_vector_channel_probe(
a: crate::export::ChannelKind,
b: crate::export::ChannelKind,
) -> crate::export::VectorChannel {
let ca = single_slot_vector_channel_probe(a);
let cb = single_slot_vector_channel_probe(b);
crate::export::VectorChannel {
http_event: ca.http_event.or(cb.http_event),
nats_subject: ca.nats_subject.or(cb.nats_subject),
stdout: ca.stdout.or(cb.stdout),
}
}
#[test]
fn production_tagged_union_kind_list_borrows_the_inherent_constant() {
assert!(std::ptr::eq(
<crate::intent::Intent as TaggedUnion>::KIND_LIST,
crate::intent::INTENT_KIND_LIST,
));
assert!(std::ptr::eq(
<crate::encapsulates::EncapsulationKind as TaggedUnion>::KIND_LIST,
crate::encapsulates::ENCAPSULATION_TARGET_LIST,
));
assert!(std::ptr::eq(
<crate::export::ArtifactSource as TaggedUnion>::KIND_LIST,
crate::export::ARTIFACT_KIND_LIST,
));
assert!(std::ptr::eq(
<crate::export::VectorChannel as TaggedUnion>::KIND_LIST,
crate::export::CHANNEL_KIND_LIST,
));
}
#[test]
fn tagged_union_default_variant_resolves_each_populated_slot() {
let mut p = LocalParent {
alpha: Some(11),
..Default::default()
};
assert_eq!(
<LocalParent as TaggedUnion>::variant(&p).unwrap(),
LocalVariant::Alpha(&11)
);
p = LocalParent {
beta: Some(22),
..Default::default()
};
assert_eq!(
<LocalParent as TaggedUnion>::variant(&p).unwrap(),
LocalVariant::Beta(&22)
);
p = LocalParent {
gamma: Some(33),
..Default::default()
};
assert_eq!(
<LocalParent as TaggedUnion>::variant(&p).unwrap(),
LocalVariant::Gamma(&33)
);
}
#[test]
fn tagged_union_default_variant_empty_carries_kind_list_by_pointer() {
let empty = LocalParent::default();
let err = <LocalParent as TaggedUnion>::variant(&empty).unwrap_err();
match err {
LocalParentError::Empty(list) => {
assert!(
std::ptr::eq(list, <LocalParent as TaggedUnion>::KIND_LIST),
"TaggedUnion::variant default must carry KIND_LIST by pointer, not by re-composition",
);
}
LocalParentError::Ambiguous => {
panic!("expected Empty carrier, got Ambiguous");
}
}
}
#[test]
fn tagged_union_default_variant_ambiguous_on_multiple_populated_slots() {
let p = LocalParent {
alpha: Some(1),
beta: Some(2),
gamma: None,
};
assert_eq!(
<LocalParent as TaggedUnion>::variant(&p).unwrap_err(),
LocalParentError::Ambiguous
);
}
#[test]
fn every_production_inherent_variant_dispatches_through_trait_default() {
use crate::encapsulates::{EncapsulationKind, EncapsulationKindError};
use crate::export::{ArtifactError, ArtifactSource, ChannelError, VectorChannel};
use crate::intent::{Intent, IntentError};
let i = Intent::default();
match (i.variant(), <Intent as TaggedUnion>::variant(&i)) {
(Err(IntentError::Empty(a)), Err(IntentError::Empty(b))) => assert!(
std::ptr::eq(a, b),
"Intent inherent and trait dispatch must return the same &'static str",
),
(a, b) => panic!("Intent inherent/trait mismatch: inherent={a:?}, trait={b:?}"),
}
let k = EncapsulationKind::default();
match (k.variant(), <EncapsulationKind as TaggedUnion>::variant(&k)) {
(Err(EncapsulationKindError::Empty(a)), Err(EncapsulationKindError::Empty(b))) => {
assert!(
std::ptr::eq(a, b),
"EncapsulationKind inherent and trait dispatch must return the same &'static str",
)
}
(a, b) => {
panic!("EncapsulationKind inherent/trait mismatch: inherent={a:?}, trait={b:?}")
}
}
let s = ArtifactSource::default();
match (s.variant(), <ArtifactSource as TaggedUnion>::variant(&s)) {
(Err(ArtifactError::Empty(a)), Err(ArtifactError::Empty(b))) => assert!(
std::ptr::eq(a, b),
"ArtifactSource inherent and trait dispatch must return the same &'static str",
),
(a, b) => panic!("ArtifactSource inherent/trait mismatch: inherent={a:?}, trait={b:?}"),
}
let c = VectorChannel::default();
match (c.variant(), <VectorChannel as TaggedUnion>::variant(&c)) {
(Err(ChannelError::Empty(a)), Err(ChannelError::Empty(b))) => assert!(
std::ptr::eq(a, b),
"VectorChannel inherent and trait dispatch must return the same &'static str",
),
(a, b) => panic!("VectorChannel inherent/trait mismatch: inherent={a:?}, trait={b:?}"),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, tatara_closed_set::DeriveClosedSet)]
#[closed_set(via = "as_str", generate_unknown)]
enum MacroLocalKind {
Foo,
Bar,
}
impl MacroLocalKind {
const ALL: [Self; 2] = [Self::Foo, Self::Bar];
const fn as_str(self) -> &'static str {
match self {
Self::Foo => "foo",
Self::Bar => "bar",
}
}
fn select<'a>(self, parent: &'a MacroLocalParent) -> Option<MacroLocalVariant<'a>> {
match self {
Self::Foo => parent.foo.as_ref().map(MacroLocalVariant::Foo),
Self::Bar => parent.bar.as_ref().map(MacroLocalVariant::Bar),
}
}
}
#[derive(Default, serde::Serialize)]
struct MacroLocalParent {
#[serde(skip_serializing_if = "Option::is_none")]
foo: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
bar: Option<u32>,
}
#[derive(Debug, PartialEq)]
enum MacroLocalVariant<'a> {
Foo(&'a u32),
Bar(&'a u32),
}
impl VariantKind<MacroLocalKind> for MacroLocalVariant<'_> {
fn variant_kind(&self) -> MacroLocalKind {
match self {
Self::Foo(_) => MacroLocalKind::Foo,
Self::Bar(_) => MacroLocalKind::Bar,
}
}
}
crate::declare_tagged_union_error! {
pub(super) MacroLocalError,
empty = "macro-local parent has no variant set (one of {0} required)",
ambiguous = "macro-local parent has multiple variants set; exactly one required",
}
const MACRO_LOCAL_KIND_LIST: &str = "foo/bar";
crate::declare_tagged_union_impls! {
parent = MacroLocalParent,
kind = MacroLocalKind,
variant = MacroLocalVariant,
error = MacroLocalError,
kind_list = MACRO_LOCAL_KIND_LIST,
}
#[test]
fn macro_emitted_tagged_union_impl_binds_kind_list_coherently() {
assert_kind_list_matches_closed_set::<MacroLocalParent>();
assert!(std::ptr::eq(
<MacroLocalParent as TaggedUnion>::KIND_LIST,
MACRO_LOCAL_KIND_LIST,
));
}
#[test]
fn macro_emitted_inherent_variant_dispatches_the_four_outcome_truth_table() {
let p = MacroLocalParent {
foo: Some(11),
bar: None,
};
assert_eq!(p.variant().unwrap(), MacroLocalVariant::Foo(&11));
let p = MacroLocalParent {
foo: None,
bar: Some(22),
};
assert_eq!(p.variant().unwrap(), MacroLocalVariant::Bar(&22));
let p = MacroLocalParent::default();
match p.variant().unwrap_err() {
MacroLocalError::Empty(list) => assert_eq!(list, MACRO_LOCAL_KIND_LIST),
MacroLocalError::Ambiguous => panic!("expected Empty, got Ambiguous"),
}
let p = MacroLocalParent {
foo: Some(1),
bar: Some(2),
};
assert_eq!(p.variant().unwrap_err(), MacroLocalError::Ambiguous);
}
#[test]
fn macro_emitted_variant_selector_delegates_to_inherent_select() {
let p = MacroLocalParent {
foo: Some(7),
bar: None,
};
let via_trait =
<MacroLocalKind as VariantSelector<MacroLocalParent>>::select(MacroLocalKind::Foo, &p)
.unwrap();
let via_inherent = MacroLocalKind::Foo.select(&p).unwrap();
match (via_trait, via_inherent) {
(MacroLocalVariant::Foo(a), MacroLocalVariant::Foo(b)) => {
assert!(
std::ptr::eq(a, b),
"macro-emitted VariantSelector::select must delegate to <Kind>::select — same borrow, not a copy",
);
}
_ => panic!("expected Foo arm on both dispatch paths"),
}
}
#[test]
fn every_production_kind_closedset_all_matches_inherent_all() {
use crate::encapsulates::EncapsulationTarget;
use crate::export::{ArtifactKind, ChannelKind};
use crate::intent::IntentKind;
assert_eq!(
<IntentKind as tatara_closed_set::ClosedSet>::ALL,
IntentKind::ALL.as_slice(),
);
assert_eq!(
<EncapsulationTarget as tatara_closed_set::ClosedSet>::ALL,
EncapsulationTarget::ALL.as_slice(),
);
assert_eq!(
<ArtifactKind as tatara_closed_set::ClosedSet>::ALL,
ArtifactKind::ALL.as_slice(),
);
assert_eq!(
<ChannelKind as tatara_closed_set::ClosedSet>::ALL,
ChannelKind::ALL.as_slice(),
);
}
#[test]
fn assert_variant_round_trip_accepts_coherent_local_impl() {
fn make_local(k: LocalKind) -> LocalParent {
match k {
LocalKind::Alpha => LocalParent {
alpha: Some(11),
..Default::default()
},
LocalKind::Beta => LocalParent {
beta: Some(22),
..Default::default()
},
LocalKind::Gamma => LocalParent {
gamma: Some(33),
..Default::default()
},
}
}
assert_variant_round_trip::<LocalParent, _>(make_local);
}
#[test]
#[should_panic(expected = "VariantSelector::select must return Some for populated slot")]
fn assert_variant_round_trip_rejects_factory_that_leaves_slot_empty() {
fn empty_factory(_: LocalKind) -> LocalParent {
LocalParent::default()
}
assert_variant_round_trip::<LocalParent, _>(empty_factory);
}
#[test]
fn assert_two_slots_ambiguous_accepts_coherent_local_impl() {
fn two_local(a: LocalKind, b: LocalKind) -> LocalParent {
let mut p = LocalParent::default();
for k in [a, b] {
match k {
LocalKind::Alpha => p.alpha = Some(11),
LocalKind::Beta => p.beta = Some(22),
LocalKind::Gamma => p.gamma = Some(33),
}
}
p
}
assert_two_slots_ambiguous::<LocalParent, _>(two_local);
}
#[test]
#[should_panic(expected = "two-slot parent must not resolve to a variant")]
fn assert_two_slots_ambiguous_rejects_factory_that_populates_only_one_slot() {
fn single_only(a: LocalKind, _: LocalKind) -> LocalParent {
let mut p = LocalParent::default();
match a {
LocalKind::Alpha => p.alpha = Some(11),
LocalKind::Beta => p.beta = Some(22),
LocalKind::Gamma => p.gamma = Some(33),
}
p
}
assert_two_slots_ambiguous::<LocalParent, _>(single_only);
}
#[test]
#[should_panic(expected = "should resolve Ambiguous")]
fn assert_two_slots_ambiguous_rejects_factory_that_populates_no_slots() {
fn empty_factory(_: LocalKind, _: LocalKind) -> LocalParent {
LocalParent::default()
}
assert_two_slots_ambiguous::<LocalParent, _>(empty_factory);
}
#[test]
fn assert_single_slot_key_matches_label_accepts_coherent_local_impl() {
fn make_local(k: LocalKind) -> LocalParent {
match k {
LocalKind::Alpha => LocalParent {
alpha: Some(11),
..Default::default()
},
LocalKind::Beta => LocalParent {
beta: Some(22),
..Default::default()
},
LocalKind::Gamma => LocalParent {
gamma: Some(33),
..Default::default()
},
}
}
assert_single_slot_key_matches_label::<LocalParent, _>(make_local);
}
#[test]
#[should_panic(expected = "wire-key drift")]
fn assert_single_slot_key_matches_label_rejects_factory_that_populates_wrong_slot() {
fn always_beta(_: LocalKind) -> LocalParent {
LocalParent {
beta: Some(22),
..Default::default()
}
}
assert_single_slot_key_matches_label::<LocalParent, _>(always_beta);
}
#[test]
#[should_panic(expected = "exactly one populated field")]
fn assert_single_slot_key_matches_label_rejects_factory_that_populates_no_slots() {
fn empty_factory(_: LocalKind) -> LocalParent {
LocalParent::default()
}
assert_single_slot_key_matches_label::<LocalParent, _>(empty_factory);
}
#[test]
#[should_panic(expected = "exactly one populated field")]
fn assert_single_slot_key_matches_label_rejects_factory_that_populates_two_slots() {
fn two_slot_factory(_: LocalKind) -> LocalParent {
LocalParent {
alpha: Some(1),
beta: Some(2),
gamma: None,
}
}
assert_single_slot_key_matches_label::<LocalParent, _>(two_slot_factory);
}
#[test]
fn assert_single_slot_key_matches_label_accepts_macro_emitted_impl() {
fn make_macro_local(k: MacroLocalKind) -> MacroLocalParent {
match k {
MacroLocalKind::Foo => MacroLocalParent {
foo: Some(7),
bar: None,
},
MacroLocalKind::Bar => MacroLocalParent {
foo: None,
bar: Some(8),
},
}
}
assert_single_slot_key_matches_label::<MacroLocalParent, _>(make_macro_local);
}
#[derive(Default, serde::Serialize)]
struct BareParent {
#[serde(skip_serializing_if = "Option::is_none")]
alpha: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
beta: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
gamma: Option<u32>,
}
#[test]
fn assert_wire_key_matches_label_accepts_coherent_bare_parent_impl() {
fn make_bare(k: LocalKind) -> BareParent {
match k {
LocalKind::Alpha => BareParent {
alpha: Some(11),
..Default::default()
},
LocalKind::Beta => BareParent {
beta: Some(22),
..Default::default()
},
LocalKind::Gamma => BareParent {
gamma: Some(33),
..Default::default()
},
}
}
assert_wire_key_matches_label::<BareParent, LocalKind, _>(make_bare);
}
#[test]
#[should_panic(expected = "wire-key drift")]
fn assert_wire_key_matches_label_rejects_factory_that_populates_wrong_slot() {
fn always_beta(_: LocalKind) -> BareParent {
BareParent {
beta: Some(22),
..Default::default()
}
}
assert_wire_key_matches_label::<BareParent, LocalKind, _>(always_beta);
}
#[test]
#[should_panic(expected = "exactly one populated field")]
fn assert_wire_key_matches_label_rejects_factory_that_populates_no_slots() {
fn empty_factory(_: LocalKind) -> BareParent {
BareParent::default()
}
assert_wire_key_matches_label::<BareParent, LocalKind, _>(empty_factory);
}
#[test]
fn assert_single_slot_key_matches_label_delegates_to_wire_key_matches_label() {
fn make_local(k: LocalKind) -> LocalParent {
match k {
LocalKind::Alpha => LocalParent {
alpha: Some(11),
..Default::default()
},
LocalKind::Beta => LocalParent {
beta: Some(22),
..Default::default()
},
LocalKind::Gamma => LocalParent {
gamma: Some(33),
..Default::default()
},
}
}
assert_single_slot_key_matches_label::<LocalParent, _>(make_local);
assert_wire_key_matches_label::<LocalParent, LocalKind, _>(make_local);
}
#[test]
fn every_production_variant_kind_impl_matches_inherent_projection() {
use crate::encapsulates::{EncapsulationKindVariant, ExistingHelmRelease};
use crate::export::{ArtifactVariant, ChannelVariant, HttpEventChannel, ReceiptsSource};
use crate::intent::{IntentVariant, NixIntent};
use crate::lifetime::{LifetimeVariant, PermanentLifetime};
let nix = NixIntent {
flake_ref: "github:a/b".into(),
attribute: "x".into(),
system: None,
attic_cache: None,
extra_args: vec![],
delegate_to_nix_build: false,
};
let iv = IntentVariant::Nix(&nix);
assert_eq!(iv.kind(), iv.variant_kind());
let perm = PermanentLifetime::default();
let lv = LifetimeVariant::Permanent(&perm);
assert_eq!(lv.kind(), lv.variant_kind());
let hr = ExistingHelmRelease {
namespace: "ns".into(),
name: "n".into(),
release_name: "r".into(),
};
let ev = EncapsulationKindVariant::ExistingHelmRelease(&hr);
assert_eq!(ev.target(), ev.variant_kind());
let rs = ReceiptsSource {};
let av = ArtifactVariant::Receipts(&rs);
assert_eq!(av.kind(), av.variant_kind());
let ch = HttpEventChannel {
endpoint: None,
signal_type: "s".into(),
};
let cv = ChannelVariant::HttpEvent(&ch);
assert_eq!(cv.kind(), cv.variant_kind());
}
}