use once_cell::sync::Lazy;
use serde::Deserialize;
use serde_yaml::from_str;
use std::collections::HashMap;
use strsim::levenshtein;
static TABLE_ID_YAML: &str = include_str!("../../../../table_id_map.yaml");
#[derive(Debug, Deserialize)]
struct TableIdConfig {
mappings: HashMap<String, String>,
}
static TABLE_ID_MAP: Lazy<HashMap<String, String>> = Lazy::new(|| {
let cfg: TableIdConfig = from_str(TABLE_ID_YAML).expect("Invalid table_id_map.yaml");
cfg.mappings
});
async fn get_uuid_columns_from_schema(table_name: &str) -> Vec<String> {
let query: String = format!(
"SELECT column_name, type FROM system_schema.columns WHERE keyspace_name = 'athena_rs' AND table_name = '{}' ALLOW FILTERING",
table_name
);
match crate::drivers::scylla::client::execute_query(query).await {
Ok((rows, _)) => rows
.iter()
.filter_map(|row| {
let column_name = row.get("column_name")?.as_str()?;
let column_type = row.get("type")?.as_str()?;
if column_type == "uuid" {
Some(column_name.to_string())
} else {
None
}
})
.collect(),
Err(err) => {
tracing::warn!("Failed to query schema for table {}: {}", table_name, err);
Vec::new()
}
}
}
#[doc(hidden)]
pub fn find_closest_uuid_column(table_name: &str, uuid_columns: &[String]) -> Option<String> {
if uuid_columns.is_empty() {
return None;
}
uuid_columns
.iter()
.map(|col| {
let distance = levenshtein(table_name, col);
(col.clone(), distance)
})
.min_by_key(|(_, distance)| *distance)
.map(|(col, _)| col)
}
pub async fn get_resource_id_key(table_name: &str) -> String {
if let Some(mapped_key) = TABLE_ID_MAP.get(table_name) {
return mapped_key.clone();
}
let uuid_columns: Vec<String> = get_uuid_columns_from_schema(table_name).await;
if let Some(closest_column) = find_closest_uuid_column(table_name, &uuid_columns) {
tracing::info!(
"Dynamic fallback for table '{}': using column '{}'",
table_name,
closest_column
);
return closest_column;
}
tracing::warn!(
"No UUID column found for table '{}', falling back to 'id'",
table_name
);
"id".to_string()
}