use std::collections::HashMap;
use helios_persistence::core::{BackendCapability, BackendKind};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SupportLevel {
Implemented,
Partial,
Planned,
NotPlanned,
RequiresExternalService,
}
impl SupportLevel {
pub fn should_run(&self) -> bool {
matches!(self, SupportLevel::Implemented | SupportLevel::Partial)
}
pub fn allows_partial(&self) -> bool {
matches!(self, SupportLevel::Partial)
}
pub fn should_skip(&self) -> bool {
!self.should_run()
}
}
impl Default for SupportLevel {
fn default() -> Self {
SupportLevel::Planned
}
}
impl std::fmt::Display for SupportLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let symbol = match self {
SupportLevel::Implemented => "✓",
SupportLevel::Partial => "◐",
SupportLevel::Planned => "○",
SupportLevel::NotPlanned => "✗",
SupportLevel::RequiresExternalService => "†",
};
write!(f, "{}", symbol)
}
}
#[derive(Debug, Clone)]
pub struct CapabilityMatrix {
matrix: HashMap<BackendKind, HashMap<BackendCapability, SupportLevel>>,
}
impl CapabilityMatrix {
pub fn new() -> Self {
Self {
matrix: HashMap::new(),
}
}
pub fn default() -> Self {
let mut matrix = Self::new();
matrix.set_backend_capabilities(
BackendKind::Sqlite,
vec![
(BackendCapability::Crud, SupportLevel::Implemented),
(BackendCapability::Versioning, SupportLevel::Implemented),
(BackendCapability::InstanceHistory, SupportLevel::Implemented),
(BackendCapability::TypeHistory, SupportLevel::Implemented),
(BackendCapability::SystemHistory, SupportLevel::Implemented),
(BackendCapability::BasicSearch, SupportLevel::Implemented),
(BackendCapability::DateSearch, SupportLevel::Implemented),
(BackendCapability::ReferenceSearch, SupportLevel::Implemented),
(BackendCapability::ChainedSearch, SupportLevel::Partial),
(BackendCapability::ReverseChaining, SupportLevel::Partial),
(BackendCapability::Include, SupportLevel::Implemented),
(BackendCapability::Revinclude, SupportLevel::Implemented),
(BackendCapability::FullTextSearch, SupportLevel::NotPlanned),
(BackendCapability::TerminologySearch, SupportLevel::RequiresExternalService),
(BackendCapability::Transactions, SupportLevel::Implemented),
(BackendCapability::OptimisticLocking, SupportLevel::Implemented),
(BackendCapability::CursorPagination, SupportLevel::Planned),
(BackendCapability::OffsetPagination, SupportLevel::Implemented),
(BackendCapability::Sorting, SupportLevel::Implemented),
(BackendCapability::BulkExport, SupportLevel::Planned),
(BackendCapability::SharedSchema, SupportLevel::Implemented),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::NotPlanned),
],
);
matrix.set_backend_capabilities(
BackendKind::Postgres,
vec![
(BackendCapability::Crud, SupportLevel::Implemented),
(BackendCapability::Versioning, SupportLevel::Implemented),
(BackendCapability::InstanceHistory, SupportLevel::Implemented),
(BackendCapability::TypeHistory, SupportLevel::Implemented),
(BackendCapability::SystemHistory, SupportLevel::Implemented),
(BackendCapability::BasicSearch, SupportLevel::Implemented),
(BackendCapability::DateSearch, SupportLevel::Implemented),
(BackendCapability::ReferenceSearch, SupportLevel::Implemented),
(BackendCapability::ChainedSearch, SupportLevel::Partial),
(BackendCapability::ReverseChaining, SupportLevel::Partial),
(BackendCapability::Include, SupportLevel::Implemented),
(BackendCapability::Revinclude, SupportLevel::Implemented),
(BackendCapability::FullTextSearch, SupportLevel::Partial),
(BackendCapability::TerminologySearch, SupportLevel::RequiresExternalService),
(BackendCapability::Transactions, SupportLevel::Implemented),
(BackendCapability::OptimisticLocking, SupportLevel::Implemented),
(BackendCapability::CursorPagination, SupportLevel::Planned),
(BackendCapability::OffsetPagination, SupportLevel::Implemented),
(BackendCapability::Sorting, SupportLevel::Implemented),
(BackendCapability::BulkExport, SupportLevel::Planned),
(BackendCapability::SharedSchema, SupportLevel::Implemented),
(BackendCapability::SchemaPerTenant, SupportLevel::Planned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix.set_backend_capabilities(
BackendKind::MongoDB,
vec![
(BackendCapability::Crud, SupportLevel::Planned),
(BackendCapability::Versioning, SupportLevel::Planned),
(BackendCapability::InstanceHistory, SupportLevel::Planned),
(BackendCapability::TypeHistory, SupportLevel::Planned),
(BackendCapability::SystemHistory, SupportLevel::Planned),
(BackendCapability::BasicSearch, SupportLevel::Planned),
(BackendCapability::DateSearch, SupportLevel::Planned),
(BackendCapability::ReferenceSearch, SupportLevel::Planned),
(BackendCapability::ChainedSearch, SupportLevel::Partial),
(BackendCapability::ReverseChaining, SupportLevel::Partial),
(BackendCapability::Include, SupportLevel::Planned),
(BackendCapability::Revinclude, SupportLevel::Planned),
(BackendCapability::FullTextSearch, SupportLevel::Implemented),
(BackendCapability::TerminologySearch, SupportLevel::RequiresExternalService),
(BackendCapability::Transactions, SupportLevel::Planned),
(BackendCapability::OptimisticLocking, SupportLevel::Planned),
(BackendCapability::CursorPagination, SupportLevel::Planned),
(BackendCapability::OffsetPagination, SupportLevel::Planned),
(BackendCapability::Sorting, SupportLevel::Planned),
(BackendCapability::BulkExport, SupportLevel::Planned),
(BackendCapability::SharedSchema, SupportLevel::Planned),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix.set_backend_capabilities(
BackendKind::Cassandra,
vec![
(BackendCapability::Crud, SupportLevel::Planned),
(BackendCapability::Versioning, SupportLevel::Planned),
(BackendCapability::InstanceHistory, SupportLevel::Planned),
(BackendCapability::TypeHistory, SupportLevel::NotPlanned),
(BackendCapability::SystemHistory, SupportLevel::NotPlanned),
(BackendCapability::BasicSearch, SupportLevel::Partial),
(BackendCapability::DateSearch, SupportLevel::Planned),
(BackendCapability::ReferenceSearch, SupportLevel::NotPlanned),
(BackendCapability::ChainedSearch, SupportLevel::NotPlanned),
(BackendCapability::ReverseChaining, SupportLevel::NotPlanned),
(BackendCapability::Include, SupportLevel::NotPlanned),
(BackendCapability::Revinclude, SupportLevel::NotPlanned),
(BackendCapability::FullTextSearch, SupportLevel::NotPlanned),
(BackendCapability::TerminologySearch, SupportLevel::NotPlanned),
(BackendCapability::Transactions, SupportLevel::NotPlanned),
(BackendCapability::OptimisticLocking, SupportLevel::Planned),
(BackendCapability::CursorPagination, SupportLevel::Planned),
(BackendCapability::OffsetPagination, SupportLevel::NotPlanned),
(BackendCapability::Sorting, SupportLevel::NotPlanned),
(BackendCapability::BulkExport, SupportLevel::Planned),
(BackendCapability::SharedSchema, SupportLevel::Planned),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix.set_backend_capabilities(
BackendKind::Neo4j,
vec![
(BackendCapability::Crud, SupportLevel::Planned),
(BackendCapability::Versioning, SupportLevel::Partial),
(BackendCapability::InstanceHistory, SupportLevel::Partial),
(BackendCapability::TypeHistory, SupportLevel::NotPlanned),
(BackendCapability::SystemHistory, SupportLevel::NotPlanned),
(BackendCapability::BasicSearch, SupportLevel::Planned),
(BackendCapability::DateSearch, SupportLevel::Planned),
(BackendCapability::ReferenceSearch, SupportLevel::Implemented),
(BackendCapability::ChainedSearch, SupportLevel::Implemented),
(BackendCapability::ReverseChaining, SupportLevel::Implemented),
(BackendCapability::Include, SupportLevel::Implemented),
(BackendCapability::Revinclude, SupportLevel::Implemented),
(BackendCapability::FullTextSearch, SupportLevel::Partial),
(BackendCapability::TerminologySearch, SupportLevel::Implemented),
(BackendCapability::Transactions, SupportLevel::Planned),
(BackendCapability::OptimisticLocking, SupportLevel::Partial),
(BackendCapability::CursorPagination, SupportLevel::Planned),
(BackendCapability::OffsetPagination, SupportLevel::Planned),
(BackendCapability::Sorting, SupportLevel::Planned),
(BackendCapability::BulkExport, SupportLevel::NotPlanned),
(BackendCapability::SharedSchema, SupportLevel::Planned),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix.set_backend_capabilities(
BackendKind::Elasticsearch,
vec![
(BackendCapability::Crud, SupportLevel::Implemented),
(BackendCapability::Versioning, SupportLevel::Partial),
(BackendCapability::InstanceHistory, SupportLevel::NotPlanned),
(BackendCapability::TypeHistory, SupportLevel::NotPlanned),
(BackendCapability::SystemHistory, SupportLevel::NotPlanned),
(BackendCapability::BasicSearch, SupportLevel::Implemented),
(BackendCapability::DateSearch, SupportLevel::Implemented),
(BackendCapability::ReferenceSearch, SupportLevel::Implemented),
(BackendCapability::ChainedSearch, SupportLevel::NotPlanned),
(BackendCapability::ReverseChaining, SupportLevel::NotPlanned),
(BackendCapability::Include, SupportLevel::Implemented),
(BackendCapability::Revinclude, SupportLevel::Implemented),
(BackendCapability::FullTextSearch, SupportLevel::Implemented),
(BackendCapability::TerminologySearch, SupportLevel::RequiresExternalService),
(BackendCapability::Transactions, SupportLevel::NotPlanned),
(BackendCapability::OptimisticLocking, SupportLevel::Implemented),
(BackendCapability::CursorPagination, SupportLevel::Implemented),
(BackendCapability::OffsetPagination, SupportLevel::Implemented),
(BackendCapability::Sorting, SupportLevel::Implemented),
(BackendCapability::BulkExport, SupportLevel::Implemented),
(BackendCapability::SharedSchema, SupportLevel::Implemented),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix.set_backend_capabilities(
BackendKind::S3,
vec![
(BackendCapability::Crud, SupportLevel::Planned),
(BackendCapability::Versioning, SupportLevel::Implemented),
(BackendCapability::InstanceHistory, SupportLevel::Implemented),
(BackendCapability::TypeHistory, SupportLevel::NotPlanned),
(BackendCapability::SystemHistory, SupportLevel::NotPlanned),
(BackendCapability::BasicSearch, SupportLevel::NotPlanned),
(BackendCapability::DateSearch, SupportLevel::NotPlanned),
(BackendCapability::ReferenceSearch, SupportLevel::NotPlanned),
(BackendCapability::ChainedSearch, SupportLevel::NotPlanned),
(BackendCapability::ReverseChaining, SupportLevel::NotPlanned),
(BackendCapability::Include, SupportLevel::NotPlanned),
(BackendCapability::Revinclude, SupportLevel::NotPlanned),
(BackendCapability::FullTextSearch, SupportLevel::NotPlanned),
(BackendCapability::TerminologySearch, SupportLevel::NotPlanned),
(BackendCapability::Transactions, SupportLevel::NotPlanned),
(BackendCapability::OptimisticLocking, SupportLevel::Implemented),
(BackendCapability::CursorPagination, SupportLevel::Partial),
(BackendCapability::OffsetPagination, SupportLevel::NotPlanned),
(BackendCapability::Sorting, SupportLevel::NotPlanned),
(BackendCapability::BulkExport, SupportLevel::Implemented),
(BackendCapability::SharedSchema, SupportLevel::Planned),
(BackendCapability::SchemaPerTenant, SupportLevel::NotPlanned),
(BackendCapability::DatabasePerTenant, SupportLevel::Planned),
],
);
matrix
}
pub fn set_backend_capabilities(
&mut self,
backend: BackendKind,
capabilities: Vec<(BackendCapability, SupportLevel)>,
) {
let map = self.matrix.entry(backend).or_insert_with(HashMap::new);
for (cap, level) in capabilities {
map.insert(cap, level);
}
}
pub fn support_level(&self, backend: BackendKind, capability: BackendCapability) -> SupportLevel {
self.matrix
.get(&backend)
.and_then(|caps| caps.get(&capability))
.copied()
.unwrap_or(SupportLevel::Planned)
}
pub fn can_test(&self, backend: BackendKind, capability: BackendCapability) -> bool {
self.support_level(backend, capability).should_run()
}
pub fn implemented_capabilities(&self, backend: BackendKind) -> Vec<BackendCapability> {
self.matrix
.get(&backend)
.map(|caps| {
caps.iter()
.filter(|(_, level)| **level == SupportLevel::Implemented)
.map(|(cap, _)| *cap)
.collect()
})
.unwrap_or_default()
}
pub fn testable_capabilities(&self, backend: BackendKind) -> Vec<BackendCapability> {
self.matrix
.get(&backend)
.map(|caps| {
caps.iter()
.filter(|(_, level)| level.should_run())
.map(|(cap, _)| *cap)
.collect()
})
.unwrap_or_default()
}
pub fn print_summary(&self) {
let all_capabilities = vec![
BackendCapability::Crud,
BackendCapability::Versioning,
BackendCapability::InstanceHistory,
BackendCapability::TypeHistory,
BackendCapability::SystemHistory,
BackendCapability::BasicSearch,
BackendCapability::DateSearch,
BackendCapability::ReferenceSearch,
BackendCapability::ChainedSearch,
BackendCapability::ReverseChaining,
BackendCapability::Include,
BackendCapability::Revinclude,
BackendCapability::FullTextSearch,
BackendCapability::TerminologySearch,
BackendCapability::Transactions,
BackendCapability::OptimisticLocking,
BackendCapability::CursorPagination,
BackendCapability::OffsetPagination,
BackendCapability::Sorting,
BackendCapability::BulkExport,
BackendCapability::SharedSchema,
BackendCapability::SchemaPerTenant,
BackendCapability::DatabasePerTenant,
];
let backends = vec![
BackendKind::Sqlite,
BackendKind::Postgres,
BackendKind::MongoDB,
BackendKind::Cassandra,
BackendKind::Neo4j,
BackendKind::Elasticsearch,
BackendKind::S3,
];
println!("\nCapability Matrix:");
println!("Legend: {} Implemented, {} Partial, {} Planned, {} NotPlanned, {} RequiresExternalService\n",
SupportLevel::Implemented,
SupportLevel::Partial,
SupportLevel::Planned,
SupportLevel::NotPlanned,
SupportLevel::RequiresExternalService,
);
print!("{:<25}", "Capability");
for backend in &backends {
print!("{:>10}", format!("{}", backend));
}
println!();
println!("{}", "-".repeat(25 + backends.len() * 10));
for cap in &all_capabilities {
print!("{:<25}", format!("{}", cap));
for backend in &backends {
let level = self.support_level(*backend, *cap);
print!("{:>10}", format!("{}", level));
}
println!();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_support_level_should_run() {
assert!(SupportLevel::Implemented.should_run());
assert!(SupportLevel::Partial.should_run());
assert!(!SupportLevel::Planned.should_run());
assert!(!SupportLevel::NotPlanned.should_run());
assert!(!SupportLevel::RequiresExternalService.should_run());
}
#[test]
fn test_support_level_display() {
assert_eq!(SupportLevel::Implemented.to_string(), "✓");
assert_eq!(SupportLevel::Partial.to_string(), "◐");
assert_eq!(SupportLevel::Planned.to_string(), "○");
assert_eq!(SupportLevel::NotPlanned.to_string(), "✗");
assert_eq!(SupportLevel::RequiresExternalService.to_string(), "†");
}
#[test]
fn test_default_matrix() {
let matrix = CapabilityMatrix::default();
assert_eq!(
matrix.support_level(BackendKind::Sqlite, BackendCapability::Crud),
SupportLevel::Implemented
);
assert_eq!(
matrix.support_level(BackendKind::Sqlite, BackendCapability::FullTextSearch),
SupportLevel::NotPlanned
);
assert_eq!(
matrix.support_level(BackendKind::Neo4j, BackendCapability::ChainedSearch),
SupportLevel::Implemented
);
}
#[test]
fn test_implemented_capabilities() {
let matrix = CapabilityMatrix::default();
let sqlite_caps = matrix.implemented_capabilities(BackendKind::Sqlite);
assert!(sqlite_caps.contains(&BackendCapability::Crud));
assert!(sqlite_caps.contains(&BackendCapability::Transactions));
assert!(!sqlite_caps.contains(&BackendCapability::FullTextSearch));
}
#[test]
fn test_testable_capabilities() {
let matrix = CapabilityMatrix::default();
let sqlite_caps = matrix.testable_capabilities(BackendKind::Sqlite);
assert!(sqlite_caps.contains(&BackendCapability::Crud));
assert!(sqlite_caps.contains(&BackendCapability::ChainedSearch));
assert!(!sqlite_caps.contains(&BackendCapability::FullTextSearch));
}
#[test]
fn test_can_test() {
let matrix = CapabilityMatrix::default();
assert!(matrix.can_test(BackendKind::Sqlite, BackendCapability::Crud));
assert!(!matrix.can_test(BackendKind::Sqlite, BackendCapability::FullTextSearch));
}
}