use crate::MetadataImage;
pub const KRABKA_VERSION_FEATURE: &str = "krabka.version";
pub const KRABKA_VERSION_MIN: i16 = 0;
pub const KRABKA_VERSION_MAX: i16 = 1;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KrabkaVersion(i16);
impl KrabkaVersion {
#[must_use]
pub const fn new(level: i16) -> Self {
Self(level)
}
#[must_use]
pub fn finalized(image: &MetadataImage) -> Self {
Self(
image
.finalized_feature(KRABKA_VERSION_FEATURE)
.unwrap_or(KRABKA_VERSION_MIN),
)
}
#[must_use]
pub const fn level(self) -> i16 {
self.0
}
#[must_use]
pub const fn max_request_version(self, rpc: PrivateRpc) -> Option<i16> {
rpc.max_request_version(self.0)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PrivateRpc {
SubmitChange,
MetadataFetch,
DelegationTokenMutation,
}
impl PrivateRpc {
pub const ALL: [Self; 3] = [
Self::SubmitChange,
Self::MetadataFetch,
Self::DelegationTokenMutation,
];
#[must_use]
pub const fn api_key(self) -> i16 {
match self {
Self::SubmitChange => 1003,
Self::MetadataFetch => 1004,
Self::DelegationTokenMutation => 1005,
}
}
#[must_use]
pub const fn from_api_key(api_key: i16) -> Option<Self> {
match api_key {
1003 => Some(Self::SubmitChange),
1004 => Some(Self::MetadataFetch),
1005 => Some(Self::DelegationTokenMutation),
_ => None,
}
}
#[must_use]
pub const fn max_request_version(self, krabka_version: i16) -> Option<i16> {
if krabka_version < KRABKA_VERSION_MIN || krabka_version > KRABKA_VERSION_MAX {
return None;
}
Some(match self {
Self::SubmitChange | Self::MetadataFetch | Self::DelegationTokenMutation => 0,
})
}
}
#[must_use]
pub const fn private_rpc_version(api_key: i16, krabka_version: i16) -> Option<i16> {
match PrivateRpc::from_api_key(api_key) {
Some(rpc) => rpc.max_request_version(krabka_version),
None => None,
}
}
#[cfg(test)]
mod tests {
use assert2::check;
use super::*;
use crate::{FeatureLevelRecord, MetadataRecord};
fn image_at(level: Option<i16>) -> MetadataImage {
let mut image = MetadataImage::new(uuid::Uuid::nil());
if let Some(level) = level {
image.apply(&MetadataRecord::V1FeatureLevel(FeatureLevelRecord {
name: KRABKA_VERSION_FEATURE.into(),
level,
}));
}
image
}
#[test]
fn constants_are_pinned() {
check!(
(
KRABKA_VERSION_FEATURE,
KRABKA_VERSION_MIN,
KRABKA_VERSION_MAX
) == ("krabka.version", 0, 1)
);
}
#[test]
fn finalized_reads_the_image() {
for (case, finalized, want) in [
("absent reads as 0", None, KrabkaVersion::new(0)),
("explicit 0", Some(0), KrabkaVersion::new(0)),
(
"maximum",
Some(KRABKA_VERSION_MAX),
KrabkaVersion::new(KRABKA_VERSION_MAX),
),
] {
check!(
KrabkaVersion::finalized(&image_at(finalized)) == want,
"{case}"
);
}
}
#[test]
fn finalized_ignores_other_features() {
let mut image = image_at(None);
image.apply(&MetadataRecord::V1FeatureLevel(FeatureLevelRecord {
name: "transaction.version".into(),
level: 2,
}));
check!(KrabkaVersion::finalized(&image).level() == 0);
}
#[test]
fn private_rpc_version_table() {
for rpc in PrivateRpc::ALL {
let (api_key, rows): (i16, [(i16, Option<i16>); 4]) = match rpc {
PrivateRpc::SubmitChange => {
(1003, [(-1, None), (0, Some(0)), (1, Some(0)), (2, None)])
}
PrivateRpc::MetadataFetch => {
(1004, [(-1, None), (0, Some(0)), (1, Some(0)), (2, None)])
}
PrivateRpc::DelegationTokenMutation => {
(1005, [(-1, None), (0, Some(0)), (1, Some(0)), (2, None)])
}
};
check!(rpc.api_key() == api_key);
check!(PrivateRpc::from_api_key(api_key) == Some(rpc));
for (level, want) in rows {
check!(
private_rpc_version(api_key, level) == want,
"{rpc:?} at {level}"
);
check!(
KrabkaVersion::new(level).max_request_version(rpc) == want,
"{rpc:?} at {level}"
);
}
}
}
#[test]
fn private_rpc_version_refuses_other_api_keys() {
for api_key in [0, 18, 1000, 1001, 1002, 1006, -1] {
check!(
private_rpc_version(api_key, 0).is_none(),
"api key {api_key}"
);
}
}
}