use serde::{Deserialize, Serialize};
#[derive(
Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, gen_platform::IsVariant,
)]
pub enum CaixaKind {
Biblioteca,
Binario,
Servico,
Supervisor,
Aplicacao,
Acao,
}
impl CaixaKind {
#[must_use]
pub const fn requires_lib(self) -> bool {
matches!(self, Self::Biblioteca)
}
#[must_use]
pub const fn requires_exe(self) -> bool {
matches!(self, Self::Binario)
}
#[must_use]
pub const fn requires_servicos(self) -> bool {
matches!(self, Self::Servico)
}
#[must_use]
pub const fn requires_children(self) -> bool {
matches!(self, Self::Supervisor)
}
#[must_use]
pub const fn requires_membros(self) -> bool {
matches!(self, Self::Aplicacao)
}
#[must_use]
pub const fn requires_ci(self) -> bool {
matches!(self, Self::Acao)
}
#[must_use]
pub const fn wire_name(self) -> &'static str {
match self {
Self::Biblioteca => crate::render::CAIXA_KIND_WIRE_BIBLIOTECA,
Self::Binario => crate::render::CAIXA_KIND_WIRE_BINARIO,
Self::Servico => crate::render::CAIXA_KIND_WIRE_SERVICO,
Self::Supervisor => crate::render::CAIXA_KIND_WIRE_SUPERVISOR,
Self::Aplicacao => crate::render::CAIXA_KIND_WIRE_APLICACAO,
Self::Acao => crate::render::CAIXA_KIND_WIRE_ACAO,
}
}
#[must_use]
pub fn from_wire(s: &str) -> Option<Self> {
match s {
crate::render::CAIXA_KIND_WIRE_BIBLIOTECA => Some(Self::Biblioteca),
crate::render::CAIXA_KIND_WIRE_BINARIO => Some(Self::Binario),
crate::render::CAIXA_KIND_WIRE_SERVICO => Some(Self::Servico),
crate::render::CAIXA_KIND_WIRE_SUPERVISOR => Some(Self::Supervisor),
crate::render::CAIXA_KIND_WIRE_APLICACAO => Some(Self::Aplicacao),
crate::render::CAIXA_KIND_WIRE_ACAO => Some(Self::Acao),
_ => None,
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Biblioteca => crate::render::CAIXA_KIND_LABEL_BIBLIOTECA,
Self::Binario => crate::render::CAIXA_KIND_LABEL_BINARIO,
Self::Servico => crate::render::CAIXA_KIND_LABEL_SERVICO,
Self::Supervisor => crate::render::CAIXA_KIND_LABEL_SUPERVISOR,
Self::Aplicacao => crate::render::CAIXA_KIND_LABEL_APLICACAO,
Self::Acao => crate::render::CAIXA_KIND_LABEL_ACAO,
}
}
}
impl std::fmt::Display for CaixaKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kind_requirements() {
assert!(CaixaKind::Biblioteca.requires_lib());
assert!(!CaixaKind::Biblioteca.requires_exe());
assert!(CaixaKind::Binario.requires_exe());
assert!(CaixaKind::Servico.requires_servicos());
assert!(CaixaKind::Supervisor.requires_children());
assert!(!CaixaKind::Servico.requires_children());
assert!(CaixaKind::Acao.requires_ci());
assert!(!CaixaKind::Servico.requires_ci());
}
#[test]
fn kind_deserializes_from_pascal_symbol() {
let v: CaixaKind = serde_json::from_str("\"Biblioteca\"").unwrap();
assert_eq!(v, CaixaKind::Biblioteca);
let v: CaixaKind = serde_json::from_str("\"Supervisor\"").unwrap();
assert_eq!(v, CaixaKind::Supervisor);
}
#[test]
fn supervisor_kind_has_canonical_name() {
assert_eq!(CaixaKind::Supervisor.as_str(), "supervisor");
}
#[test]
fn caixa_kind_as_str_returns_lifted_peer_const() {
for (variant, expected) in [
(
CaixaKind::Biblioteca,
crate::render::CAIXA_KIND_LABEL_BIBLIOTECA,
),
(CaixaKind::Binario, crate::render::CAIXA_KIND_LABEL_BINARIO),
(CaixaKind::Servico, crate::render::CAIXA_KIND_LABEL_SERVICO),
(
CaixaKind::Supervisor,
crate::render::CAIXA_KIND_LABEL_SUPERVISOR,
),
(
CaixaKind::Aplicacao,
crate::render::CAIXA_KIND_LABEL_APLICACAO,
),
(CaixaKind::Acao, crate::render::CAIXA_KIND_LABEL_ACAO),
] {
assert_eq!(
variant.as_str(),
expected,
"CaixaKind::{variant:?}.as_str() must return the lifted \
CAIXA_KIND_LABEL_* const"
);
}
}
#[test]
fn caixa_kind_display_routes_through_as_str_helper() {
for variant in [
CaixaKind::Biblioteca,
CaixaKind::Binario,
CaixaKind::Servico,
CaixaKind::Supervisor,
CaixaKind::Aplicacao,
CaixaKind::Acao,
] {
assert_eq!(
variant.to_string(),
variant.as_str(),
"CaixaKind::{variant:?} Display must route through \
CaixaKind::as_str (single source of truth: the lifted \
CAIXA_KIND_LABEL_* const)"
);
}
}
#[test]
fn caixa_kind_display_matches_as_str_and_not_serialize_wire() {
for variant in [
CaixaKind::Biblioteca,
CaixaKind::Binario,
CaixaKind::Servico,
CaixaKind::Supervisor,
CaixaKind::Aplicacao,
CaixaKind::Acao,
] {
let wire = serde_json::to_string(&variant).unwrap();
let unquoted = wire
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.expect("serialized CaixaKind is a JSON string");
let display = variant.to_string();
assert_eq!(
display,
variant.as_str(),
"CaixaKind::{variant:?} Display must byte-equal as_str"
);
assert_ne!(
display, unquoted,
"CaixaKind::{variant:?} Display / as_str (lowercase Portuguese \
diagnostic form) must stay structurally distinct from the \
Serialize wire format (PascalCase tatara-lisp author surface)"
);
assert!(
unquoted.chars().next().is_some_and(char::is_uppercase),
"CaixaKind::{variant:?} wire format must open with an \
uppercase byte (PascalCase tatara-lisp author surface)"
);
assert!(
display.chars().next().is_some_and(char::is_lowercase),
"CaixaKind::{variant:?} Display / as_str must open with a \
lowercase byte (Portuguese diagnostic form)"
);
}
}
#[test]
fn caixa_kind_label_consts_pin_canonical_bytes() {
assert_eq!(crate::render::CAIXA_KIND_LABEL_BIBLIOTECA, "biblioteca");
assert_eq!(crate::render::CAIXA_KIND_LABEL_BINARIO, "binario");
assert_eq!(crate::render::CAIXA_KIND_LABEL_SERVICO, "servico");
assert_eq!(crate::render::CAIXA_KIND_LABEL_SUPERVISOR, "supervisor");
assert_eq!(crate::render::CAIXA_KIND_LABEL_APLICACAO, "aplicacao");
assert_eq!(crate::render::CAIXA_KIND_LABEL_ACAO, "acao");
}
#[test]
fn caixa_kind_is_variant_predicates_partition_the_arm_set() {
let rows: [(CaixaKind, [bool; 6]); 6] = [
(
CaixaKind::Biblioteca,
[true, false, false, false, false, false],
),
(
CaixaKind::Binario,
[false, true, false, false, false, false],
),
(
CaixaKind::Servico,
[false, false, true, false, false, false],
),
(
CaixaKind::Supervisor,
[false, false, false, true, false, false],
),
(
CaixaKind::Aplicacao,
[false, false, false, false, true, false],
),
(CaixaKind::Acao, [false, false, false, false, false, true]),
];
for (variant, expected) in rows {
let observed = [
variant.is_biblioteca(),
variant.is_binario(),
variant.is_servico(),
variant.is_supervisor(),
variant.is_aplicacao(),
variant.is_acao(),
];
assert_eq!(
observed, expected,
"CaixaKind::{variant:?} is_* predicates must partition \
the arm set (biblioteca, binario, servico, supervisor, \
aplicacao, acao); got {observed:?}"
);
}
}
#[test]
fn caixa_kind_is_variant_predicates_are_const_fn() {
const IS_APLICACAO: bool = CaixaKind::Aplicacao.is_aplicacao();
const IS_BIBLIOTECA: bool = CaixaKind::Biblioteca.is_biblioteca();
const IS_SERVICO: bool = CaixaKind::Servico.is_servico();
const IS_SUPERVISOR: bool = CaixaKind::Supervisor.is_supervisor();
const IS_BINARIO: bool = CaixaKind::Binario.is_binario();
const IS_ACAO: bool = CaixaKind::Acao.is_acao();
assert!(IS_APLICACAO);
assert!(IS_BIBLIOTECA);
assert!(IS_SERVICO);
assert!(IS_SUPERVISOR);
assert!(IS_BINARIO);
assert!(IS_ACAO);
}
#[test]
fn caixa_kind_wire_name_returns_lifted_peer_const() {
for (variant, expected) in [
(
CaixaKind::Biblioteca,
crate::render::CAIXA_KIND_WIRE_BIBLIOTECA,
),
(CaixaKind::Binario, crate::render::CAIXA_KIND_WIRE_BINARIO),
(CaixaKind::Servico, crate::render::CAIXA_KIND_WIRE_SERVICO),
(
CaixaKind::Supervisor,
crate::render::CAIXA_KIND_WIRE_SUPERVISOR,
),
(
CaixaKind::Aplicacao,
crate::render::CAIXA_KIND_WIRE_APLICACAO,
),
(CaixaKind::Acao, crate::render::CAIXA_KIND_WIRE_ACAO),
] {
assert_eq!(
variant.wire_name(),
expected,
"CaixaKind::{variant:?}.wire_name() must return the \
lifted CAIXA_KIND_WIRE_* const"
);
}
}
#[test]
fn caixa_kind_wire_name_matches_serialize_wire_byte_string() {
for variant in [
CaixaKind::Biblioteca,
CaixaKind::Binario,
CaixaKind::Servico,
CaixaKind::Supervisor,
CaixaKind::Aplicacao,
CaixaKind::Acao,
] {
let json = serde_json::to_string(&variant).unwrap();
let unquoted = json
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.expect("serialized CaixaKind is a JSON string");
assert_eq!(
variant.wire_name(),
unquoted,
"CaixaKind::{variant:?}.wire_name() must byte-equal the \
un-renamed Serialize derive's wire scalar — a mismatch \
means either the derive attributes drifted or the \
CAIXA_KIND_WIRE_* const family drifted; either way \
downstream K8s-CR round-trip through caixa_from_cr \
silently splits from the accessor-routed source of \
truth"
);
}
}
#[test]
fn caixa_kind_wire_round_trips_through_from_wire() {
for variant in [
CaixaKind::Biblioteca,
CaixaKind::Binario,
CaixaKind::Servico,
CaixaKind::Supervisor,
CaixaKind::Aplicacao,
CaixaKind::Acao,
] {
let wire = variant.wire_name();
let parsed = CaixaKind::from_wire(wire).unwrap_or_else(|| {
panic!(
"CaixaKind::from_wire({wire:?}) must accept every \
CaixaKind::wire_name output — got None for the \
wire byte-string of {variant:?}"
)
});
assert_eq!(
parsed, variant,
"CaixaKind::from_wire(CaixaKind::{variant:?}.wire_name()) \
must return CaixaKind::{variant:?} — the (wire_name, \
from_wire) pair must form a total round-trip on the \
closed six-arm CaixaKind arm-set"
);
}
}
#[test]
fn caixa_kind_from_wire_rejects_unknown_byte_strings() {
for bad in [
"",
"biblioteca",
"servico",
"aplicacao",
"ACao",
"unknown",
"actor",
"Biblioteka",
] {
assert!(
CaixaKind::from_wire(bad).is_none(),
"CaixaKind::from_wire({bad:?}) must return None — the \
parser's accept-set is exactly the six CaixaKind::wire_name \
outputs; a widening would silently split the parser's \
accept-set from the emitter's"
);
}
}
#[test]
fn caixa_kind_wire_name_is_const_fn() {
const BIBLIOTECA_WIRE: &str = CaixaKind::Biblioteca.wire_name();
const BINARIO_WIRE: &str = CaixaKind::Binario.wire_name();
const SERVICO_WIRE: &str = CaixaKind::Servico.wire_name();
const SUPERVISOR_WIRE: &str = CaixaKind::Supervisor.wire_name();
const APLICACAO_WIRE: &str = CaixaKind::Aplicacao.wire_name();
const ACAO_WIRE: &str = CaixaKind::Acao.wire_name();
assert_eq!(BIBLIOTECA_WIRE, "Biblioteca");
assert_eq!(BINARIO_WIRE, "Binario");
assert_eq!(SERVICO_WIRE, "Servico");
assert_eq!(SUPERVISOR_WIRE, "Supervisor");
assert_eq!(APLICACAO_WIRE, "Aplicacao");
assert_eq!(ACAO_WIRE, "Acao");
}
#[test]
fn caixa_kind_wire_consts_are_pairwise_distinct() {
let all = [
crate::render::CAIXA_KIND_WIRE_BIBLIOTECA,
crate::render::CAIXA_KIND_WIRE_BINARIO,
crate::render::CAIXA_KIND_WIRE_SERVICO,
crate::render::CAIXA_KIND_WIRE_SUPERVISOR,
crate::render::CAIXA_KIND_WIRE_APLICACAO,
crate::render::CAIXA_KIND_WIRE_ACAO,
];
for (i, a) in all.iter().enumerate() {
for (j, b) in all.iter().enumerate() {
if i != j {
assert_ne!(
a, b,
"CAIXA_KIND_WIRE_* consts must be pairwise \
distinct — found duplicate byte-string {a:?} \
at indices {i} and {j}"
);
}
}
}
}
#[test]
fn caixa_kind_label_consts_are_pairwise_distinct() {
let all = [
crate::render::CAIXA_KIND_LABEL_BIBLIOTECA,
crate::render::CAIXA_KIND_LABEL_BINARIO,
crate::render::CAIXA_KIND_LABEL_SERVICO,
crate::render::CAIXA_KIND_LABEL_SUPERVISOR,
crate::render::CAIXA_KIND_LABEL_APLICACAO,
crate::render::CAIXA_KIND_LABEL_ACAO,
];
for (i, a) in all.iter().enumerate() {
for (j, b) in all.iter().enumerate() {
if i != j {
assert_ne!(
a, b,
"CAIXA_KIND_LABEL_* consts must be pairwise distinct \
— found duplicate byte-string {a:?} at indices {i} \
and {j}"
);
}
}
}
}
}