use std::collections::HashMap;
use crate::tree::ast::dataset_identifier::{DatasetIdentifier, UnqualifiedDatasetIdentifier};
use crate::tree::ast::identifier::SimpleIdentifier;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum CatalogKeyMatchingError {
#[error("ambiguous catalog replacement for {0}")]
AmbiguousReplacement(String),
}
pub fn catalog_key_aliases(
catalog_key: &str,
default_space: Option<&SimpleIdentifier>,
) -> Vec<String> {
let mut aliases = vec![catalog_key.to_string()];
if let Ok(id) = DatasetIdentifier::from_ref_str(catalog_key) {
aliases.push(id.to_string());
if let Some(qualified) = id.maybe_qualify(default_space) {
aliases.push(qualified.to_string());
}
if let DatasetIdentifier::Qualified(q) = id {
if default_space.is_some_and(|space| space.as_str() == q.space.as_str()) {
let unqual = UnqualifiedDatasetIdentifier {
namespace: q.namespace,
table: q.table,
};
aliases.push(unqual.to_string());
}
}
}
aliases.sort();
aliases.dedup();
aliases
}
pub fn catalog_keys_for_dataset(
dataset: &DatasetIdentifier,
default_space: Option<&SimpleIdentifier>,
) -> Vec<String> {
let mut keys = vec![dataset.to_string()];
if let Some(qualified) = dataset.maybe_qualify(default_space) {
keys.push(qualified.to_string());
}
keys
}
pub fn find_catalog_replacement(
dataset: &DatasetIdentifier,
replacements: &HashMap<String, String>,
default_space: Option<&SimpleIdentifier>,
) -> Result<Option<String>, CatalogKeyMatchingError> {
let ref_keys = catalog_keys_for_dataset(dataset, default_space);
let mut matched_cte: Option<String> = None;
for (catalog_key, cte_name) in replacements {
let aliases = catalog_key_aliases(catalog_key, default_space);
if !ref_keys
.iter()
.any(|key| aliases.iter().any(|alias| alias == key))
{
continue;
}
match &matched_cte {
None => matched_cte = Some(cte_name.clone()),
Some(existing) if existing == cte_name => {}
Some(_) => {
return Err(CatalogKeyMatchingError::AmbiguousReplacement(
dataset.to_string(),
));
}
}
}
Ok(matched_cte)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn qualified_key_does_not_alias_bare_table_without_default_space() {
let aliases = catalog_key_aliases("events:foo", None);
assert!(!aliases.contains(&"foo".to_string()));
}
#[test]
fn qualified_key_aliases_bare_table_when_default_space_matches() {
let default_space = SimpleIdentifier::new("events");
let aliases = catalog_key_aliases("events:foo", Some(&default_space));
assert!(aliases.contains(&"foo".to_string()));
}
#[test]
fn qualified_key_does_not_alias_bare_table_for_other_default_space() {
let default_space = SimpleIdentifier::new("signals");
let aliases = catalog_key_aliases("events:foo", Some(&default_space));
assert!(!aliases.contains(&"foo".to_string()));
}
#[test]
fn unqualified_ref_with_default_space_does_not_match_other_space_key() {
let dataset = DatasetIdentifier::from_ref_str("foo").unwrap();
let mut replacements = HashMap::new();
replacements.insert("events:foo".to_string(), "events_foo".to_string());
replacements.insert("signals:foo".to_string(), "signals_foo".to_string());
let default_space = SimpleIdentifier::new("events");
let matched =
find_catalog_replacement(&dataset, &replacements, Some(&default_space)).unwrap();
assert_eq!(matched, Some("events_foo".to_string()));
}
}