pub(crate) use crabka_metadata::metadata_version::METADATA_VERSION_FEATURE as METADATA_VERSION;
#[allow(unused_imports)]
pub(crate) use crabka_metadata::metadata_version::METADATA_VERSION_MAX;
#[allow(unused_imports)]
pub(crate) use crabka_metadata::metadata_version::METADATA_VERSION_MIN;
use crabka_metadata::MetadataImage;
#[allow(unused_imports)]
pub(crate) use crabka_metadata::metadata_version::SHARE_VERSION_FEATURE as SHARE_VERSION;
pub(crate) use crabka_metadata::metadata_version::STREAMS_VERSION_FEATURE as STREAMS_VERSION;
#[derive(Debug, Clone, Copy)]
pub(crate) struct SupportedFeature {
pub name: &'static str,
pub min_version: i16,
pub max_version: i16,
}
pub(crate) fn supported_features() -> Vec<SupportedFeature> {
crabka_metadata::feature_registry()
.iter()
.map(|f| {
let (min_version, max_version) = f.supported_range();
SupportedFeature {
name: f.name(),
min_version,
max_version,
}
})
.collect()
}
#[allow(dead_code)]
pub(crate) fn lookup(name: &str) -> Option<SupportedFeature> {
crabka_metadata::feature(name).map(|f| {
let (min_version, max_version) = f.supported_range();
SupportedFeature {
name: f.name(),
min_version,
max_version,
}
})
}
pub(crate) fn require_feature(
image: &MetadataImage,
name: &str,
required_level: i16,
) -> Result<(), i16> {
let finalized = image.finalized_features().get(name).copied();
if finalized.is_some_and(|level| level < required_level) {
Err(crate::codes::UNSUPPORTED_VERSION)
} else {
Ok(())
}
}
pub(crate) fn feature_enabled(
image: &crabka_metadata::MetadataImage,
name: &str,
level: i16,
) -> bool {
image.finalized_features().get(name).copied().unwrap_or(0) >= level
}
#[cfg(test)]
mod tests {
use super::*;
use assert2::assert;
#[test]
fn feature_enabled_treats_absence_as_disabled() {
use crabka_metadata::{FeatureLevelRecord, MetadataRecord};
let mut image = MetadataImage::new(uuid::Uuid::nil());
assert!(!feature_enabled(&image, "group.version", 1)); image.apply(&MetadataRecord::V1FeatureLevel(FeatureLevelRecord {
name: "group.version".into(),
level: 1,
}));
assert!(feature_enabled(&image, "group.version", 1)); }
#[test]
fn supported_features_include_metadata_version() {
let f = lookup(METADATA_VERSION).expect("metadata.version supported");
assert!(f.min_version == METADATA_VERSION_MIN);
assert!(f.max_version == METADATA_VERSION_MAX);
assert!(lookup("not.a.feature").is_none());
}
#[test]
fn share_version_is_supported() {
let f = lookup(SHARE_VERSION).expect("share.version supported");
assert!(f.min_version == 0);
assert!(f.max_version == 1);
assert!(
supported_features()
.iter()
.any(|f| f.name == SHARE_VERSION && f.min_version == 0 && f.max_version == 1)
);
}
#[test]
fn streams_version_is_supported() {
let f = lookup(STREAMS_VERSION).expect("streams.version supported");
assert!(f.min_version == 0);
assert!(f.max_version == 1);
assert!(
supported_features()
.iter()
.any(|f| f.name == STREAMS_VERSION && f.min_version == 0 && f.max_version == 1)
);
}
#[test]
fn require_feature_is_permissive_on_unfinalized() {
let image = MetadataImage::new(uuid::Uuid::nil());
assert!(require_feature(&image, METADATA_VERSION, 11).is_ok());
}
#[test]
fn require_feature_gates_below_level() {
use crabka_metadata::{FeatureLevelRecord, MetadataRecord};
let mut image = MetadataImage::new(uuid::Uuid::nil());
image.apply(&MetadataRecord::V1FeatureLevel(FeatureLevelRecord {
name: METADATA_VERSION.to_string(),
level: 10,
}));
assert!(
require_feature(&image, METADATA_VERSION, 11) == Err(crate::codes::UNSUPPORTED_VERSION)
);
assert!(require_feature(&image, METADATA_VERSION, 10).is_ok());
assert!(require_feature(&image, METADATA_VERSION, 7).is_ok());
}
}