use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::sync::Arc;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SchemaId(pub Uuid);
impl SchemaId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl FromStr for SchemaId {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(Uuid::parse_str(s)?))
}
}
impl Default for SchemaId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Schema {
pub id: SchemaId,
pub database: String,
pub tables: Vec<Table>,
pub functions: Vec<Function>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_uri: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Table {
pub name: String,
pub columns: Vec<Column>,
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_location: Option<(String, u32)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Column {
pub name: String,
pub data_type: String,
pub nullable: bool,
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_location: Option<(String, u32)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Function {
pub name: String,
pub parameters: Vec<FunctionParameter>,
pub return_type: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionParameter {
pub name: String,
pub data_type: String,
pub optional: bool,
}
#[derive(Debug, Clone)]
pub struct SchemaManager {
schemas: Arc<DashMap<SchemaId, Schema>>,
}
impl SchemaManager {
pub fn new() -> Self {
Self {
schemas: Arc::new(DashMap::new()),
}
}
pub fn register(&self, schema: Schema) -> SchemaId {
let id = schema.id;
self.schemas.insert(id, schema);
id
}
pub fn get(&self, id: SchemaId) -> Option<Schema> {
self.schemas.get(&id).map(|s| s.clone())
}
pub fn update(&self, id: SchemaId, schema: Schema) -> bool {
if self.schemas.contains_key(&id) {
self.schemas.insert(id, schema);
true
} else {
false
}
}
pub fn remove(&self, id: SchemaId) -> bool {
self.schemas.remove(&id).is_some()
}
pub fn list_ids(&self) -> Vec<SchemaId> {
self.schemas.iter().map(|entry| *entry.key()).collect()
}
pub fn clear(&self) {
self.schemas.clear();
}
}
impl Default for SchemaManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_schema_id() {
let id1 = SchemaId::new();
let id2 = SchemaId::new();
assert_ne!(id1, id2);
let id_str = id1.0.to_string();
let id3 = SchemaId::from_str(&id_str).unwrap();
assert_eq!(id1, id3);
}
#[test]
fn test_schema_manager() {
let manager = SchemaManager::new();
let schema = Schema {
id: SchemaId::new(),
database: "test_db".to_string(),
tables: vec![],
functions: vec![],
source_uri: None,
};
let id = manager.register(schema.clone());
assert_eq!(id, schema.id);
let retrieved = manager.get(id).unwrap();
assert_eq!(retrieved.database, "test_db");
manager.remove(id);
assert!(manager.get(id).is_none());
}
#[tokio::test]
async fn test_schema_manager_concurrent() {
let manager = SchemaManager::new();
let manager_clone = manager.clone();
let schema1 = Schema {
id: SchemaId::new(),
database: "db1".to_string(),
tables: vec![],
functions: vec![],
source_uri: None,
};
let schema2 = Schema {
id: SchemaId::new(),
database: "db2".to_string(),
tables: vec![],
functions: vec![],
source_uri: None,
};
let id1 = manager.register(schema1);
let id2 = manager_clone.register(schema2);
assert_eq!(manager.get(id1).unwrap().database, "db1");
assert_eq!(manager_clone.get(id2).unwrap().database, "db2");
assert_ne!(id1, id2);
}
}