use anyhow::Result;
use chrono::Local;
use std::collections::HashMap;
use crate::ir::Config;
pub mod json;
pub mod postgres;
pub mod prisma;
pub trait Backend {
fn name(&self) -> &'static str;
fn file_extension(&self) -> &'static str;
fn generate(&self, cfg: &Config, strict: bool) -> Result<String>;
}
pub struct BackendRegistry {
backends: HashMap<String, Box<dyn Backend>>,
}
impl BackendRegistry {
pub fn new() -> Self {
Self {
backends: HashMap::new(),
}
}
pub fn register(&mut self, backend: Box<dyn Backend>) {
let name = backend.name().to_string().to_lowercase();
self.backends.insert(name, backend);
}
pub fn register_alias(&mut self, alias: &str, backend: Box<dyn Backend>) {
self.backends.insert(alias.to_lowercase(), backend);
}
pub fn get(&self, name: &str) -> Option<&dyn Backend> {
self.backends.get(&name.to_lowercase()).map(|b| &**b)
}
pub fn list_backends(&self) -> Vec<&str> {
self.backends.keys().map(|s| s.as_str()).collect()
}
}
impl Default for BackendRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn generate_header_comment(backend_name: &str, comment_style: CommentStyle) -> String {
let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S %Z").to_string();
let warning = format!(
"WARNING: This file is auto-generated by dbschema.\n\
Do not edit this file manually. Any changes will be overwritten\n\
the next time the schema is regenerated.\n\
\n\
Backend: {}\n\
Generated: {}",
backend_name, timestamp
);
match comment_style {
CommentStyle::Sql => {
let lines: Vec<String> = warning.lines().map(|line| format!("-- {}", line)).collect();
format!("{}\n\n", lines.join("\n"))
}
CommentStyle::Prisma => {
let lines: Vec<String> = warning.lines().map(|line| format!("// {}", line)).collect();
format!("{}\n\n", lines.join("\n"))
}
}
}
pub enum CommentStyle {
Sql,
Prisma,
}
pub fn get_default_backend_registry() -> BackendRegistry {
let mut registry = BackendRegistry::new();
let provider_registry = crate::provider::get_default_provider_registry();
for provider_name in provider_registry.list_providers() {
if let Some(provider) = provider_registry.get(provider_name) {
provider.register_backends(&mut registry);
}
}
registry.register(Box::new(json::JsonBackend));
registry.register(Box::new(prisma::PrismaBackend));
registry
}
pub fn get_backend(name: &str) -> Option<Box<dyn Backend>> {
match name.to_lowercase().as_str() {
"postgres" | "pg" => Some(Box::new(postgres::PostgresBackend)),
"json" => Some(Box::new(json::JsonBackend)),
"prisma" => Some(Box::new(prisma::PrismaBackend)),
_ => None,
}
}