use crate::schemasync::{PreservationMode, mockmake::coordinate::CoordinationGroup};
use bon::Builder;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use tracing::{debug, trace};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PluginConfig {
pub path: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, Builder)]
pub struct SchemasyncConfig {
pub database: DatabaseConfig,
pub should_generate_mocks: bool,
pub mock_gen_config: SchemasyncMockGenConfig,
pub performance: PerformanceConfig,
#[serde(default)]
#[builder(default)]
pub plugins: BTreeMap<String, PluginConfig>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum DatabaseProvider {
#[default]
Surrealdb,
Postgres,
Mysql,
Sqlite,
}
impl std::fmt::Display for DatabaseProvider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DatabaseProvider::Surrealdb => write!(f, "surrealdb"),
DatabaseProvider::Postgres => write!(f, "postgres"),
DatabaseProvider::Mysql => write!(f, "mysql"),
DatabaseProvider::Sqlite => write!(f, "sqlite"),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DatabaseConfig {
#[serde(default)]
pub provider: DatabaseProvider,
pub url: String,
#[serde(default)]
pub namespace: String,
#[serde(default)]
pub database: String,
#[serde(default = "default_timeout")]
pub timeout: u64,
#[serde(default)]
pub accesses: AccessesSource,
#[serde(default)]
pub functions: Option<FunctionsSource>,
#[serde(skip)]
pub resolved: ResolvedDatabaseItems,
#[serde(default)]
pub max_connections: Option<u32>,
#[serde(default)]
pub min_connections: Option<u32>,
#[serde(default)]
pub schema: Option<String>,
}
fn default_timeout() -> u64 {
60
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AccessConfig {
pub name: String,
pub access_type: AccessType,
pub table_name: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub enum AccessType {
System,
Record,
Bearer,
Jwt,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum AccessesSource {
Inline(Vec<AccessConfig>),
Path { path: String },
}
impl Default for AccessesSource {
fn default() -> Self {
AccessesSource::Inline(vec![])
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FunctionsSource {
pub path: String,
}
#[derive(Debug, Clone, Default)]
pub struct ResolvedDatabaseItems {
pub access_surql: Option<String>,
pub functions_surql: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, Builder)]
pub struct SchemasyncMockGenConfig {
pub default_record_count: usize,
pub default_preservation_mode: PreservationMode,
pub default_batch_size: usize,
#[builder(default)]
pub coordination_groups: Vec<CoordinationGroup>,
pub full_refresh_mode: bool,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
provider: DatabaseProvider::default(),
url: String::new(),
namespace: String::new(),
database: String::new(),
timeout: default_timeout(),
accesses: AccessesSource::default(),
functions: None,
resolved: ResolvedDatabaseItems::default(),
max_connections: None,
min_connections: None,
schema: None,
}
}
}
impl DatabaseConfig {
pub fn for_testing() -> Self {
debug!("Creating database configuration for testing environment");
let config = Self {
provider: DatabaseProvider::Surrealdb,
url: "http://localhost:8000".to_string(),
namespace: "test".to_string(),
database: "test".to_string(),
accesses: AccessesSource::Inline(vec![AccessConfig {
name: "user".to_owned(),
access_type: AccessType::Record,
table_name: "user".to_owned(),
}]),
functions: None,
resolved: ResolvedDatabaseItems::default(),
timeout: 60,
max_connections: None,
min_connections: None,
schema: None,
};
trace!(
"Test database config - URL: {}, namespace: {}, database: {}, timeout: {}s",
config.url, config.namespace, config.database, config.timeout
);
if let AccessesSource::Inline(ref accesses) = config.accesses {
trace!("Test access configs: {} entries", accesses.len());
}
config
}
pub fn for_postgres_testing(url: &str) -> Self {
debug!("Creating PostgreSQL database configuration for testing");
Self {
provider: DatabaseProvider::Postgres,
url: url.to_string(),
namespace: String::new(),
database: String::new(),
timeout: 60,
accesses: AccessesSource::default(),
functions: None,
resolved: ResolvedDatabaseItems::default(),
max_connections: Some(5),
min_connections: Some(1),
schema: Some("public".to_string()),
}
}
pub fn for_sqlite_testing(path: &str) -> Self {
debug!("Creating SQLite database configuration for testing");
Self {
provider: DatabaseProvider::Sqlite,
url: format!("sqlite:{}", path),
namespace: String::new(),
database: String::new(),
timeout: 60,
accesses: AccessesSource::default(),
functions: None,
resolved: ResolvedDatabaseItems::default(),
max_connections: Some(1),
min_connections: Some(1),
schema: None,
}
}
#[cfg(feature = "schemasync")]
pub fn to_provider_config(&self) -> crate::schemasync::database::DatabaseConfig {
crate::schemasync::database::DatabaseConfig {
provider: match self.provider {
DatabaseProvider::Surrealdb => crate::schemasync::database::ProviderType::SurrealDb,
DatabaseProvider::Postgres => crate::schemasync::database::ProviderType::Postgres,
DatabaseProvider::Mysql => crate::schemasync::database::ProviderType::MySql,
DatabaseProvider::Sqlite => crate::schemasync::database::ProviderType::Sqlite,
},
url: self.url.clone(),
namespace: if self.namespace.is_empty() {
None
} else {
Some(self.namespace.clone())
},
database: if self.database.is_empty() {
None
} else {
Some(self.database.clone())
},
username: None, password: None, max_connections: self.max_connections,
min_connections: self.min_connections,
schema: self.schema.clone(),
timeout_secs: self.timeout,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
pub embedded_db_memory_limit: String,
pub cache_duration_seconds: u64,
pub use_progressive_loading: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MockMode {
Smart,
RegenerateAll,
PreserveAll,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegenerateFieldsConfig {
pub always: Vec<String>,
}
impl Default for RegenerateFieldsConfig {
fn default() -> Self {
debug!("Creating default regenerate fields configuration");
let config = Self {
always: vec!["updated_at".to_string(), "created_at".to_string()],
};
trace!("Default regenerate fields: {:?}", config.always);
config
}
}
impl Default for PerformanceConfig {
fn default() -> Self {
debug!("Creating default performance configuration");
let config = Self {
embedded_db_memory_limit: "1GB".to_string(),
cache_duration_seconds: 300,
use_progressive_loading: true,
};
trace!(
"Default performance config - memory: {}, cache: {}s, progressive: {}",
config.embedded_db_memory_limit,
config.cache_duration_seconds,
config.use_progressive_loading
);
config
}
}