use crate::capacity::DEFAULT_TABLE_CAPACITY;
use crate::config::AppConfig;
use crate::error::{SqawkError, SqawkResult};
use crate::table::Table;
use std::collections::HashMap;
pub struct Database {
tables: HashMap<String, Table>,
}
impl Database {
pub fn remove_table(&mut self, name: &str) -> bool {
self.tables.remove(name).is_some()
}
}
impl Default for Database {
fn default() -> Self {
Self::new()
}
}
impl Database {
pub fn new() -> Self {
Database {
tables: HashMap::with_capacity(DEFAULT_TABLE_CAPACITY),
}
}
pub fn add_table(&mut self, name: String, table: Table) -> SqawkResult<()> {
if self.tables.contains_key(&name) {
return Err(SqawkError::TableAlreadyExists(name));
}
self.tables.insert(name, table);
Ok(())
}
pub fn get_table(&self, name: &str) -> SqawkResult<&Table> {
self.tables
.get(name)
.ok_or_else(|| SqawkError::TableNotFound(name.to_string()))
}
pub fn get_table_mut(&mut self, name: &str) -> SqawkResult<&mut Table> {
self.tables
.get_mut(name)
.ok_or_else(|| SqawkError::TableNotFound(name.to_string()))
}
pub fn table_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.tables.keys().cloned().collect();
names.sort();
names
}
pub fn table_count(&self) -> usize {
self.tables.len()
}
pub fn has_table(&self, name: &str) -> bool {
self.tables.contains_key(name)
}
pub fn compile_table_definitions(&mut self, config: &AppConfig) -> SqawkResult<()> {
for tabledef in config.table_definitions() {
if let Some((table_name, columns_str)) = tabledef.split_once(':') {
let columns = columns_str
.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<String>>();
if !columns.is_empty() {
let columns_len = columns.len(); let table = Table::new(table_name, columns, None);
if self.has_table(table_name) {
self.tables.remove(table_name);
}
self.add_table(table_name.to_string(), table)?;
if config.verbose() {
println!(
"Compiled table definition for '{}' with {} columns",
table_name, columns_len
);
}
}
} else if config.verbose() {
eprintln!("Invalid table definition format: {}", tabledef);
eprintln!("Expected format: table_name:col1,col2,...");
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_and_get_table() {
let mut db = Database::new();
let table = Table::new("test", vec!["id".to_string(), "name".to_string()], None);
db.add_table("test".to_string(), table).unwrap();
let table = db.get_table("test").unwrap();
assert_eq!(table.name(), "test");
assert_eq!(table.columns(), &["id", "name"]);
}
#[test]
fn test_table_operations() {
let mut db = Database::new();
let table1 = Table::new("table1", vec!["col1".to_string()], None);
let table2 = Table::new("table2", vec!["col2".to_string()], None);
db.add_table("table1".to_string(), table1).unwrap();
db.add_table("table2".to_string(), table2).unwrap();
assert_eq!(db.table_count(), 2);
let names = db.table_names();
assert!(names.contains(&"table1".to_string()));
assert!(names.contains(&"table2".to_string()));
assert!(db.has_table("table1"));
assert!(db.has_table("table2"));
db.tables.remove("table1");
assert_eq!(db.table_count(), 1);
assert!(!db.has_table("table1"));
assert!(db.has_table("table2"));
}
}