#[derive(Debug, Clone)]
pub struct ExtensionRegistry {
built_in: Vec<String>,
custom: Vec<String>,
}
impl ExtensionRegistry {
pub fn new() -> Self {
let mut built_in = Vec::new();
#[cfg(feature = "experimental_batch")]
built_in.push("batch".to_string());
#[cfg(feature = "experimental_publish_delay")]
built_in.push("publish-delay".to_string());
#[cfg(feature = "experimental_media_query")]
built_in.push("media-query".to_string());
#[cfg(feature = "experimental_channels")]
built_in.push("channels".to_string());
#[cfg(feature = "experimental_syndication")]
built_in.push("syndication".to_string());
#[cfg(feature = "experimental_relation")]
built_in.push("relation".to_string());
Self {
built_in,
custom: Vec::new(),
}
}
pub fn extensions(&self) -> Vec<String> {
let mut exts = self.built_in.clone();
exts.extend(self.custom.clone());
exts
}
pub fn add(&mut self, extension: String) {
if !self.built_in.contains(&extension) && !self.custom.contains(&extension) {
self.custom.push(extension);
}
}
}
impl Default for ExtensionRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_creates_empty_registry_without_features() {
let registry = ExtensionRegistry::new();
#[cfg(not(any(
feature = "experimental_batch",
feature = "experimental_publish_delay",
feature = "experimental_media_query",
feature = "experimental_channels",
feature = "experimental_syndication",
feature = "experimental_relation"
)))]
assert!(registry.extensions().is_empty());
}
#[test]
fn test_default_is_same_as_new() {
let registry_new = ExtensionRegistry::new();
let registry_default = ExtensionRegistry::default();
assert_eq!(registry_new.extensions(), registry_default.extensions());
}
#[test]
fn test_add_extension() {
let mut registry = ExtensionRegistry::new();
registry.add("custom-extension".to_string());
let extensions = registry.extensions();
assert!(extensions.contains(&"custom-extension".to_string()));
}
#[test]
fn test_add_prevents_duplicates() {
let mut registry = ExtensionRegistry::new();
registry.add("custom-extension".to_string());
registry.add("custom-extension".to_string());
registry.add("custom-extension".to_string());
let extensions = registry.extensions();
let count = extensions
.iter()
.filter(|e| *e == "custom-extension")
.count();
assert_eq!(count, 1);
}
#[test]
fn test_add_does_not_duplicate_builtin() {
let mut registry = ExtensionRegistry::new();
#[cfg(feature = "experimental_batch")]
{
registry.add("batch".to_string());
let extensions = registry.extensions();
let count = extensions.iter().filter(|e| *e == "batch").count();
assert_eq!(count, 1, "batch should only appear once");
}
#[cfg(not(feature = "experimental_batch"))]
{
registry.add("batch".to_string());
let extensions = registry.extensions();
assert!(extensions.contains(&"batch".to_string()));
}
}
#[test]
fn test_extensions_returns_built_in_first() {
let mut registry = ExtensionRegistry::new();
registry.add("zzz-custom".to_string());
registry.add("aaa-custom".to_string());
let extensions = registry.extensions();
let custom_positions: Vec<_> = extensions
.iter()
.enumerate()
.filter(|(_, e)| e.ends_with("-custom"))
.map(|(i, _)| i)
.collect();
#[cfg(any(
feature = "experimental_batch",
feature = "experimental_publish_delay",
feature = "experimental_media_query",
feature = "experimental_channels",
feature = "experimental_syndication",
feature = "experimental_relation"
))]
{
let builtin_count = extensions
.iter()
.filter(|e| !e.ends_with("-custom"))
.count();
for pos in custom_positions {
assert!(
pos >= builtin_count,
"Custom extension should come after built-in"
);
}
}
}
#[test]
fn test_multiple_custom_extensions() {
let mut registry = ExtensionRegistry::new();
registry.add("ext-one".to_string());
registry.add("ext-two".to_string());
registry.add("ext-three".to_string());
let extensions = registry.extensions();
assert!(extensions.contains(&"ext-one".to_string()));
assert!(extensions.contains(&"ext-two".to_string()));
assert!(extensions.contains(&"ext-three".to_string()));
}
#[test]
fn test_clone() {
let mut registry = ExtensionRegistry::new();
registry.add("cloned-ext".to_string());
let cloned = registry.clone();
assert_eq!(registry.extensions(), cloned.extensions());
}
#[test]
#[cfg(feature = "experimental_batch")]
fn test_batch_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"batch".to_string()),
"batch extension should be detected with experimental_batch feature"
);
}
#[test]
#[cfg(feature = "experimental_publish_delay")]
fn test_publish_delay_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"publish-delay".to_string()),
"publish-delay extension should be detected with experimental_publish_delay feature"
);
}
#[test]
#[cfg(feature = "experimental_media_query")]
fn test_media_query_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"media-query".to_string()),
"media-query extension should be detected with experimental_media_query feature"
);
}
#[test]
#[cfg(feature = "experimental_channels")]
fn test_channels_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"channels".to_string()),
"channels extension should be detected with experimental_channels feature"
);
}
#[test]
#[cfg(feature = "experimental_syndication")]
fn test_syndication_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"syndication".to_string()),
"syndication extension should be detected with experimental_syndication feature"
);
}
#[test]
#[cfg(feature = "experimental_relation")]
fn test_relation_feature_detected() {
let registry = ExtensionRegistry::new();
let extensions = registry.extensions();
assert!(
extensions.contains(&"relation".to_string()),
"relation extension should be detected with experimental_relation feature"
);
}
}