use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct CollationConfig {
pub enabled: bool,
pub fallback_locale: String,
pub allowed_locales: Vec<String>,
pub on_invalid_locale: InvalidLocaleStrategy,
#[serde(skip_serializing_if = "Option::is_none")]
pub database_overrides: Option<DatabaseCollationOverrides>,
}
impl Default for CollationConfig {
fn default() -> Self {
Self {
enabled: true,
fallback_locale: "en-US".to_string(),
allowed_locales: vec![
"en-US".into(),
"en-GB".into(),
"fr-FR".into(),
"de-DE".into(),
"es-ES".into(),
"ja-JP".into(),
"zh-CN".into(),
"pt-BR".into(),
"it-IT".into(),
],
on_invalid_locale: InvalidLocaleStrategy::Fallback,
database_overrides: None,
}
}
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum InvalidLocaleStrategy {
#[default]
Fallback,
DatabaseDefault,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseCollationOverrides {
#[serde(skip_serializing_if = "Option::is_none")]
pub postgres: Option<PostgresCollationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mysql: Option<MySqlCollationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sqlite: Option<SqliteCollationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sqlserver: Option<SqlServerCollationConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostgresCollationConfig {
pub use_icu: bool,
pub provider: String,
}
impl Default for PostgresCollationConfig {
fn default() -> Self {
Self {
use_icu: true,
provider: "icu".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MySqlCollationConfig {
pub charset: String,
pub suffix: String,
}
impl Default for MySqlCollationConfig {
fn default() -> Self {
Self {
charset: "utf8mb4".to_string(),
suffix: "_unicode_ci".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SqliteCollationConfig {
pub use_nocase: bool,
}
impl Default for SqliteCollationConfig {
fn default() -> Self {
Self { use_nocase: true }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SqlServerCollationConfig {
pub case_insensitive: bool,
pub accent_insensitive: bool,
}
impl Default for SqlServerCollationConfig {
fn default() -> Self {
Self {
case_insensitive: true,
accent_insensitive: true,
}
}
}
#[allow(clippy::unwrap_used)] #[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collation_config_default_fallback_locale_is_en_us() {
let config = CollationConfig::default();
assert_eq!(config.fallback_locale, "en-US");
}
#[test]
fn test_collation_config_default_is_enabled() {
let config = CollationConfig::default();
assert!(config.enabled, "CollationConfig should be enabled by default");
}
#[test]
fn test_collation_config_default_allowed_locales_contains_common_locales() {
let config = CollationConfig::default();
assert!(config.allowed_locales.contains(&"en-US".to_string()));
assert!(config.allowed_locales.contains(&"fr-FR".to_string()));
assert!(config.allowed_locales.contains(&"de-DE".to_string()));
}
#[test]
fn test_collation_config_round_trip_serde() {
let config = CollationConfig::default();
let json = serde_json::to_string(&config).unwrap();
let restored: CollationConfig = serde_json::from_str(&json).unwrap();
assert_eq!(restored.fallback_locale, config.fallback_locale);
assert_eq!(restored.enabled, config.enabled);
assert_eq!(restored.allowed_locales, config.allowed_locales);
}
#[test]
fn test_collation_config_custom_locale() {
let config = CollationConfig {
fallback_locale: "ja-JP".to_string(),
..CollationConfig::default()
};
assert_eq!(config.fallback_locale, "ja-JP");
}
#[test]
fn test_invalid_locale_strategy_default_is_fallback() {
let strategy = InvalidLocaleStrategy::default();
assert_eq!(strategy, InvalidLocaleStrategy::Fallback);
}
#[test]
fn test_postgres_collation_config_default_uses_icu() {
let config = PostgresCollationConfig::default();
assert!(config.use_icu, "PostgreSQL default collation should use ICU");
assert_eq!(config.provider, "icu");
}
#[test]
fn test_mysql_collation_config_default_charset() {
let config = MySqlCollationConfig::default();
assert_eq!(config.charset, "utf8mb4");
assert_eq!(config.suffix, "_unicode_ci");
}
#[test]
fn test_sqlite_collation_config_default_nocase() {
let config = SqliteCollationConfig::default();
assert!(config.use_nocase, "SQLite default collation should use NOCASE");
}
#[test]
fn test_sqlserver_collation_config_default_case_and_accent_insensitive() {
let config = SqlServerCollationConfig::default();
assert!(config.case_insensitive);
assert!(config.accent_insensitive);
}
}