use std::fmt;
use std::str::FromStr;
pub const ANN_KIND: &str = "eu.pulseengine.varve.kind";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum PayloadKind {
#[default]
Tool,
Crate,
Wit,
ZephyrModule,
Sdk,
WasmComponent,
Vsix,
Layer,
}
impl PayloadKind {
pub fn is_dispatchable(self) -> bool {
matches!(self, PayloadKind::Tool)
}
pub fn as_str(self) -> &'static str {
match self {
PayloadKind::Tool => "tool",
PayloadKind::Crate => "crate",
PayloadKind::Wit => "wit",
PayloadKind::ZephyrModule => "zephyr-module",
PayloadKind::Sdk => "sdk",
PayloadKind::WasmComponent => "wasm-component",
PayloadKind::Vsix => "vsix",
PayloadKind::Layer => "layer",
}
}
}
impl fmt::Display for PayloadKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error(
"unknown payload kind '{0}': this varve does not know how to handle it \
(expected one of tool, crate, wit, zephyr-module, sdk, wasm-component, vsix)"
)]
pub struct UnknownKind(pub String);
impl FromStr for PayloadKind {
type Err = UnknownKind;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"tool" => Ok(PayloadKind::Tool),
"crate" => Ok(PayloadKind::Crate),
"wit" => Ok(PayloadKind::Wit),
"zephyr-module" => Ok(PayloadKind::ZephyrModule),
"sdk" => Ok(PayloadKind::Sdk),
"wasm-component" => Ok(PayloadKind::WasmComponent),
"vsix" => Ok(PayloadKind::Vsix),
"layer" => Ok(PayloadKind::Layer),
other => Err(UnknownKind(other.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_KINDS: &[PayloadKind] = &[
PayloadKind::Tool,
PayloadKind::Crate,
PayloadKind::Wit,
PayloadKind::ZephyrModule,
PayloadKind::Sdk,
PayloadKind::WasmComponent,
PayloadKind::Vsix,
PayloadKind::Layer,
];
fn index_in_all_kinds(k: PayloadKind) -> usize {
match k {
PayloadKind::Tool => 0,
PayloadKind::Crate => 1,
PayloadKind::Wit => 2,
PayloadKind::ZephyrModule => 3,
PayloadKind::Sdk => 4,
PayloadKind::WasmComponent => 5,
PayloadKind::Vsix => 6,
PayloadKind::Layer => 7,
}
}
#[test]
fn the_kind_list_the_other_tests_iterate_holds_every_variant() {
for (i, k) in ALL_KINDS.iter().enumerate() {
assert_eq!(
index_in_all_kinds(*k),
i,
"ALL_KINDS is out of step with the enum at {k}"
);
}
}
#[test]
fn every_kind_round_trips_through_its_wire_string() {
for k in ALL_KINDS {
assert_eq!(k.as_str().parse::<PayloadKind>().unwrap(), *k);
}
assert_eq!(PayloadKind::Vsix.as_str(), "vsix");
assert_eq!("vsix".parse::<PayloadKind>().unwrap(), PayloadKind::Vsix);
assert_eq!(PayloadKind::Vsix.to_string(), "vsix");
}
#[test]
fn an_unknown_kind_is_refused_not_guessed() {
let err = "quantum-blob".parse::<PayloadKind>().unwrap_err();
assert_eq!(err, UnknownKind("quantum-blob".into()));
assert!(
err.to_string().contains("vsix"),
"the hint must list every kind this varve accepts: {err}"
);
}
#[test]
fn the_default_kind_is_tool_for_back_compat() {
assert_eq!(PayloadKind::default(), PayloadKind::Tool);
}
#[test]
fn only_a_tool_is_dispatched_by_name() {
assert!(PayloadKind::Tool.is_dispatchable());
for held in ALL_KINDS.iter().filter(|k| **k != PayloadKind::Tool) {
assert!(
!held.is_dispatchable(),
"{held} is not dispatched by name and must not be keyed by one"
);
}
assert!(
!PayloadKind::Vsix.is_dispatchable(),
"a .vsix is data handed to `code`, never a binary varve dispatches"
);
}
}