use std::collections::HashSet;
use std::sync::Arc;
use hamelin_lib::tree::{
ast::identifier::{Identifier, SimpleIdentifier},
typed_ast::{environment::TypeEnvironment, query::TypedStatement},
};
pub trait HasName {
fn has_name(&self, id: &SimpleIdentifier) -> bool;
}
#[derive(Debug)]
pub struct UniqueNameGenerator {
prefix: &'static str,
counter: usize,
}
impl UniqueNameGenerator {
pub fn new(prefix: &'static str) -> Self {
Self { prefix, counter: 0 }
}
pub fn next(&mut self, namespace: &impl HasName) -> SimpleIdentifier {
loop {
let name = format!("{}_{}", self.prefix, self.counter);
self.counter += 1;
let id = SimpleIdentifier::new(name);
if !namespace.has_name(&id) {
return id;
}
}
}
}
impl HasName for HashSet<String> {
fn has_name(&self, id: &SimpleIdentifier) -> bool {
self.contains(id.as_str())
}
}
impl HasName for TypeEnvironment {
fn has_name(&self, id: &SimpleIdentifier) -> bool {
self.lookup(&id.clone().into()).is_some()
}
}
impl HasName for Arc<TypeEnvironment> {
fn has_name(&self, id: &SimpleIdentifier) -> bool {
self.as_ref().has_name(id)
}
}
impl HasName for TypedStatement {
fn has_name(&self, id: &SimpleIdentifier) -> bool {
self.pipeline_defs.iter().any(|pd| {
pd.name
.valid_ref()
.map(|name| matches!(name, Identifier::Simple(s) if s == id))
.unwrap_or(false)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use hamelin_lib::types::INT;
#[test]
fn test_generates_sequential_names() {
let mut gen = UniqueNameGenerator::new("__test");
let schema = TypeEnvironment::default();
assert_eq!(gen.next(&schema).as_str(), "__test_0");
assert_eq!(gen.next(&schema).as_str(), "__test_1");
assert_eq!(gen.next(&schema).as_str(), "__test_2");
}
#[test]
fn test_different_prefixes_are_independent() {
let mut alias_gen = UniqueNameGenerator::new("__alias");
let mut from_gen = UniqueNameGenerator::new("__from");
let schema = TypeEnvironment::default();
assert_eq!(alias_gen.next(&schema).as_str(), "__alias_0");
assert_eq!(from_gen.next(&schema).as_str(), "__from_0");
assert_eq!(alias_gen.next(&schema).as_str(), "__alias_1");
assert_eq!(from_gen.next(&schema).as_str(), "__from_1");
}
#[test]
fn test_skips_existing_names_in_schema() {
let mut gen = UniqueNameGenerator::new("__test");
let schema = TypeEnvironment::default()
.with_str("__test_0", INT.clone())
.with_str("__test_2", INT.clone());
assert_eq!(gen.next(&schema).as_str(), "__test_1");
assert_eq!(gen.next(&schema).as_str(), "__test_3");
assert_eq!(gen.next(&schema).as_str(), "__test_4");
}
}