fraiseql_db/
collation_config.rs1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(default)]
13pub struct CollationConfig {
14 pub enabled: bool,
16
17 pub fallback_locale: String,
19
20 pub allowed_locales: Vec<String>,
22
23 pub on_invalid_locale: InvalidLocaleStrategy,
25
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub database_overrides: Option<DatabaseCollationOverrides>,
29}
30
31impl Default for CollationConfig {
32 fn default() -> Self {
33 Self {
34 enabled: true,
35 fallback_locale: "en-US".to_string(),
36 allowed_locales: vec![
37 "en-US".into(),
38 "en-GB".into(),
39 "fr-FR".into(),
40 "de-DE".into(),
41 "es-ES".into(),
42 "ja-JP".into(),
43 "zh-CN".into(),
44 "pt-BR".into(),
45 "it-IT".into(),
46 ],
47 on_invalid_locale: InvalidLocaleStrategy::Fallback,
48 database_overrides: None,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
55#[serde(rename_all = "snake_case")]
56#[non_exhaustive]
57pub enum InvalidLocaleStrategy {
58 #[default]
60 Fallback,
61 DatabaseDefault,
63 Error,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DatabaseCollationOverrides {
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub postgres: Option<PostgresCollationConfig>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub mysql: Option<MySqlCollationConfig>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub sqlite: Option<SqliteCollationConfig>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub sqlserver: Option<SqlServerCollationConfig>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct PostgresCollationConfig {
90 pub use_icu: bool,
92
93 pub provider: String,
95}
96
97impl Default for PostgresCollationConfig {
98 fn default() -> Self {
99 Self {
100 use_icu: true,
101 provider: "icu".to_string(),
102 }
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct MySqlCollationConfig {
109 pub charset: String,
111
112 pub suffix: String,
114}
115
116impl Default for MySqlCollationConfig {
117 fn default() -> Self {
118 Self {
119 charset: "utf8mb4".to_string(),
120 suffix: "_unicode_ci".to_string(),
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct SqliteCollationConfig {
128 pub use_nocase: bool,
130}
131
132impl Default for SqliteCollationConfig {
133 fn default() -> Self {
134 Self { use_nocase: true }
135 }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct SqlServerCollationConfig {
141 pub case_insensitive: bool,
143
144 pub accent_insensitive: bool,
146}
147
148impl Default for SqlServerCollationConfig {
149 fn default() -> Self {
150 Self {
151 case_insensitive: true,
152 accent_insensitive: true,
153 }
154 }
155}
156
157#[allow(clippy::unwrap_used)] #[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_collation_config_default_fallback_locale_is_en_us() {
164 let config = CollationConfig::default();
165 assert_eq!(config.fallback_locale, "en-US");
166 }
167
168 #[test]
169 fn test_collation_config_default_is_enabled() {
170 let config = CollationConfig::default();
171 assert!(config.enabled, "CollationConfig should be enabled by default");
172 }
173
174 #[test]
175 fn test_collation_config_default_allowed_locales_contains_common_locales() {
176 let config = CollationConfig::default();
177 assert!(config.allowed_locales.contains(&"en-US".to_string()));
178 assert!(config.allowed_locales.contains(&"fr-FR".to_string()));
179 assert!(config.allowed_locales.contains(&"de-DE".to_string()));
180 }
181
182 #[test]
183 fn test_collation_config_round_trip_serde() {
184 let config = CollationConfig::default();
185 let json = serde_json::to_string(&config).unwrap();
186 let restored: CollationConfig = serde_json::from_str(&json).unwrap();
187
188 assert_eq!(restored.fallback_locale, config.fallback_locale);
189 assert_eq!(restored.enabled, config.enabled);
190 assert_eq!(restored.allowed_locales, config.allowed_locales);
191 }
192
193 #[test]
194 fn test_collation_config_custom_locale() {
195 let config = CollationConfig {
196 fallback_locale: "ja-JP".to_string(),
197 ..CollationConfig::default()
198 };
199 assert_eq!(config.fallback_locale, "ja-JP");
200 }
201
202 #[test]
203 fn test_invalid_locale_strategy_default_is_fallback() {
204 let strategy = InvalidLocaleStrategy::default();
205 assert_eq!(strategy, InvalidLocaleStrategy::Fallback);
206 }
207
208 #[test]
209 fn test_postgres_collation_config_default_uses_icu() {
210 let config = PostgresCollationConfig::default();
211 assert!(config.use_icu, "PostgreSQL default collation should use ICU");
212 assert_eq!(config.provider, "icu");
213 }
214
215 #[test]
216 fn test_mysql_collation_config_default_charset() {
217 let config = MySqlCollationConfig::default();
218 assert_eq!(config.charset, "utf8mb4");
219 assert_eq!(config.suffix, "_unicode_ci");
220 }
221
222 #[test]
223 fn test_sqlite_collation_config_default_nocase() {
224 let config = SqliteCollationConfig::default();
225 assert!(config.use_nocase, "SQLite default collation should use NOCASE");
226 }
227
228 #[test]
229 fn test_sqlserver_collation_config_default_case_and_accent_insensitive() {
230 let config = SqlServerCollationConfig::default();
231 assert!(config.case_insensitive);
232 assert!(config.accent_insensitive);
233 }
234}