1use parking_lot::RwLock;
27use std::collections::HashMap;
28use std::hash::{Hash, Hasher};
29use std::sync::Arc;
30
31use crate::ast::Schema;
32use crate::error::SchemaResult;
33use crate::parser::parse_schema;
34
35#[derive(Debug, Default)]
44pub struct SchemaCache {
45 cache: RwLock<HashMap<u64, Arc<Schema>>>,
46 stats: RwLock<CacheStats>,
47}
48
49#[derive(Debug, Clone, Default)]
51pub struct CacheStats {
52 pub hits: u64,
54 pub misses: u64,
56 pub cached_count: usize,
58}
59
60impl CacheStats {
61 pub fn hit_rate(&self) -> f64 {
63 let total = self.hits + self.misses;
64 if total == 0 {
65 0.0
66 } else {
67 self.hits as f64 / total as f64
68 }
69 }
70}
71
72impl SchemaCache {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_capacity(capacity: usize) -> Self {
80 Self {
81 cache: RwLock::new(HashMap::with_capacity(capacity)),
82 stats: RwLock::default(),
83 }
84 }
85
86 pub fn get_or_parse(&self, source: &str) -> SchemaResult<Arc<Schema>> {
90 let hash = hash_source(source);
91
92 {
94 let cache = self.cache.read();
95 if let Some(schema) = cache.get(&hash) {
96 self.stats.write().hits += 1;
97 return Ok(Arc::clone(schema));
98 }
99 }
100
101 let schema = parse_schema(source)?;
103 let schema = Arc::new(schema);
104
105 {
106 let mut cache = self.cache.write();
107 cache.insert(hash, Arc::clone(&schema));
108 }
109
110 {
111 let mut stats = self.stats.write();
112 stats.misses += 1;
113 stats.cached_count = self.cache.read().len();
114 }
115
116 Ok(schema)
117 }
118
119 pub fn contains(&self, source: &str) -> bool {
121 let hash = hash_source(source);
122 self.cache.read().contains_key(&hash)
123 }
124
125 pub fn clear(&self) {
127 self.cache.write().clear();
128 self.stats.write().cached_count = 0;
129 }
130
131 pub fn stats(&self) -> CacheStats {
133 let mut stats = self.stats.read().clone();
134 stats.cached_count = self.cache.read().len();
135 stats
136 }
137
138 pub fn len(&self) -> usize {
140 self.cache.read().len()
141 }
142
143 pub fn is_empty(&self) -> bool {
145 self.cache.read().is_empty()
146 }
147
148 pub fn remove(&self, source: &str) -> bool {
150 let hash = hash_source(source);
151 self.cache.write().remove(&hash).is_some()
152 }
153}
154
155fn hash_source(source: &str) -> u64 {
157 use std::collections::hash_map::DefaultHasher;
158 let mut hasher = DefaultHasher::new();
159 source.hash(&mut hasher);
160 hasher.finish()
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Hash)]
172pub struct DocString(Arc<str>);
173
174impl DocString {
175 pub fn new(s: impl AsRef<str>) -> Self {
177 Self(Arc::from(s.as_ref()))
178 }
179
180 pub fn intern(s: impl AsRef<str>) -> Self {
185 DOC_INTERNER.intern(s.as_ref())
186 }
187
188 pub fn as_str(&self) -> &str {
190 &self.0
191 }
192
193 pub fn as_arc(&self) -> &Arc<str> {
195 &self.0
196 }
197}
198
199impl AsRef<str> for DocString {
200 fn as_ref(&self) -> &str {
201 &self.0
202 }
203}
204
205impl std::fmt::Display for DocString {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 write!(f, "{}", self.0)
208 }
209}
210
211impl From<&str> for DocString {
212 fn from(s: &str) -> Self {
213 Self::new(s)
214 }
215}
216
217impl From<String> for DocString {
218 fn from(s: String) -> Self {
219 Self(Arc::from(s))
220 }
221}
222
223static DOC_INTERNER: std::sync::LazyLock<DocInterner> = std::sync::LazyLock::new(DocInterner::new);
225
226#[derive(Debug, Default)]
228struct DocInterner {
229 strings: RwLock<HashMap<u64, Arc<str>>>,
230}
231
232impl DocInterner {
233 fn new() -> Self {
234 Self::default()
235 }
236
237 fn intern(&self, s: &str) -> DocString {
238 let hash = hash_source(s);
239
240 {
242 let strings = self.strings.read();
243 if let Some(arc) = strings.get(&hash) {
244 return DocString(Arc::clone(arc));
245 }
246 }
247
248 let arc: Arc<str> = Arc::from(s);
250 {
251 let mut strings = self.strings.write();
252 strings.insert(hash, Arc::clone(&arc));
253 }
254
255 DocString(arc)
256 }
257}
258
259#[derive(Debug, Clone, Default)]
267pub struct LazyFieldAttrs {
268 computed: std::sync::OnceLock<FieldAttrsCache>,
269}
270
271#[derive(Debug, Clone, Default)]
273pub struct FieldAttrsCache {
274 pub is_id: bool,
276 pub is_auto: bool,
278 pub is_unique: bool,
280 pub is_indexed: bool,
282 pub is_updated_at: bool,
284 pub default_value: Option<String>,
286 pub mapped_name: Option<String>,
288}
289
290impl LazyFieldAttrs {
291 pub const fn new() -> Self {
293 Self {
294 computed: std::sync::OnceLock::new(),
295 }
296 }
297
298 pub fn get_or_init<F>(&self, f: F) -> &FieldAttrsCache
300 where
301 F: FnOnce() -> FieldAttrsCache,
302 {
303 self.computed.get_or_init(f)
304 }
305
306 pub fn is_computed(&self) -> bool {
308 self.computed.get().is_some()
309 }
310
311 pub fn reset(&mut self) {
313 *self = Self::new();
314 }
315}
316
317#[derive(Debug, Default)]
326pub struct ValidationTypePool {
327 pub string_validators: HashMap<&'static str, Arc<ValidatorDef>>,
329 pub numeric_validators: HashMap<&'static str, Arc<ValidatorDef>>,
331}
332
333#[derive(Debug, Clone)]
335pub struct ValidatorDef {
336 pub name: &'static str,
338 pub validator_type: ValidatorType,
340}
341
342#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub enum ValidatorType {
345 StringFormat,
347 StringLength,
349 NumericRange,
351 Pattern,
353 Custom,
355}
356
357impl ValidationTypePool {
358 pub fn new() -> Self {
360 let mut pool = Self::default();
361 pool.init_common_validators();
362 pool
363 }
364
365 fn init_common_validators(&mut self) {
366 let string_formats = [
368 "email", "url", "uuid", "cuid", "cuid2", "nanoid", "ulid", "ipv4", "ipv6", "date",
369 "datetime", "time",
370 ];
371
372 for name in string_formats {
373 self.string_validators.insert(
374 name,
375 Arc::new(ValidatorDef {
376 name,
377 validator_type: ValidatorType::StringFormat,
378 }),
379 );
380 }
381
382 let numeric_validators = [
384 "min",
385 "max",
386 "positive",
387 "negative",
388 "nonNegative",
389 "nonPositive",
390 ];
391 for name in numeric_validators {
392 self.numeric_validators.insert(
393 name,
394 Arc::new(ValidatorDef {
395 name,
396 validator_type: ValidatorType::NumericRange,
397 }),
398 );
399 }
400 }
401
402 pub fn get_string_validator(&self, name: &str) -> Option<&Arc<ValidatorDef>> {
404 self.string_validators.get(name)
405 }
406
407 pub fn get_numeric_validator(&self, name: &str) -> Option<&Arc<ValidatorDef>> {
409 self.numeric_validators.get(name)
410 }
411}
412
413pub static VALIDATION_POOL: std::sync::LazyLock<ValidationTypePool> =
415 std::sync::LazyLock::new(ValidationTypePool::new);
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn test_schema_cache_hit() {
423 let cache = SchemaCache::new();
424
425 let schema1 = cache.get_or_parse("model User { id Int @id }").unwrap();
426 let schema2 = cache.get_or_parse("model User { id Int @id }").unwrap();
427
428 assert!(Arc::ptr_eq(&schema1, &schema2));
430
431 let stats = cache.stats();
432 assert_eq!(stats.hits, 1);
433 assert_eq!(stats.misses, 1);
434 }
435
436 #[test]
437 fn test_schema_cache_miss() {
438 let cache = SchemaCache::new();
439
440 let _ = cache.get_or_parse("model User { id Int @id }").unwrap();
441 let _ = cache.get_or_parse("model Post { id Int @id }").unwrap();
442
443 let stats = cache.stats();
444 assert_eq!(stats.hits, 0);
445 assert_eq!(stats.misses, 2);
446 }
447
448 #[test]
449 fn test_schema_cache_clear() {
450 let cache = SchemaCache::new();
451
452 let _ = cache.get_or_parse("model User { id Int @id }").unwrap();
453 assert_eq!(cache.len(), 1);
454
455 cache.clear();
456 assert_eq!(cache.len(), 0);
457 }
458
459 #[test]
460 fn test_doc_string_interning() {
461 let doc1 = DocString::intern("User profile information");
462 let doc2 = DocString::intern("User profile information");
463
464 assert!(Arc::ptr_eq(doc1.as_arc(), doc2.as_arc()));
466 }
467
468 #[test]
469 fn test_doc_string_different() {
470 let doc1 = DocString::intern("User profile");
471 let doc2 = DocString::intern("Post content");
472
473 assert_ne!(doc1.as_str(), doc2.as_str());
474 }
475
476 #[test]
477 fn test_lazy_field_attrs() {
478 let lazy = LazyFieldAttrs::new();
479
480 assert!(!lazy.is_computed());
481
482 let attrs = lazy.get_or_init(|| FieldAttrsCache {
483 is_id: true,
484 is_auto: true,
485 ..Default::default()
486 });
487
488 assert!(lazy.is_computed());
489 assert!(attrs.is_id);
490 assert!(attrs.is_auto);
491 }
492
493 #[test]
494 fn test_validation_pool() {
495 let pool = ValidationTypePool::new();
496
497 assert!(pool.get_string_validator("email").is_some());
498 assert!(pool.get_string_validator("url").is_some());
499 assert!(pool.get_numeric_validator("min").is_some());
500 assert!(pool.get_numeric_validator("max").is_some());
501 }
502
503 #[test]
504 fn test_cache_stats_hit_rate() {
505 let stats = CacheStats {
506 hits: 8,
507 misses: 2,
508 cached_count: 5,
509 };
510
511 assert!((stats.hit_rate() - 0.8).abs() < 0.001);
512 }
513
514 #[test]
515 fn test_cache_stats_zero() {
516 let stats = CacheStats::default();
517 assert_eq!(stats.hit_rate(), 0.0);
518 }
519}