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> =
225 std::sync::LazyLock::new(DocInterner::new);
226
227#[derive(Debug, Default)]
229struct DocInterner {
230 strings: RwLock<HashMap<u64, Arc<str>>>,
231}
232
233impl DocInterner {
234 fn new() -> Self {
235 Self::default()
236 }
237
238 fn intern(&self, s: &str) -> DocString {
239 let hash = hash_source(s);
240
241 {
243 let strings = self.strings.read();
244 if let Some(arc) = strings.get(&hash) {
245 return DocString(Arc::clone(arc));
246 }
247 }
248
249 let arc: Arc<str> = Arc::from(s);
251 {
252 let mut strings = self.strings.write();
253 strings.insert(hash, Arc::clone(&arc));
254 }
255
256 DocString(arc)
257 }
258}
259
260#[derive(Debug, Clone, Default)]
268pub struct LazyFieldAttrs {
269 computed: std::sync::OnceLock<FieldAttrsCache>,
270}
271
272#[derive(Debug, Clone, Default)]
274pub struct FieldAttrsCache {
275 pub is_id: bool,
277 pub is_auto: bool,
279 pub is_unique: bool,
281 pub is_indexed: bool,
283 pub is_updated_at: bool,
285 pub default_value: Option<String>,
287 pub mapped_name: Option<String>,
289}
290
291impl LazyFieldAttrs {
292 pub const fn new() -> Self {
294 Self {
295 computed: std::sync::OnceLock::new(),
296 }
297 }
298
299 pub fn get_or_init<F>(&self, f: F) -> &FieldAttrsCache
301 where
302 F: FnOnce() -> FieldAttrsCache,
303 {
304 self.computed.get_or_init(f)
305 }
306
307 pub fn is_computed(&self) -> bool {
309 self.computed.get().is_some()
310 }
311
312 pub fn reset(&mut self) {
314 *self = Self::new();
315 }
316}
317
318#[derive(Debug, Default)]
327pub struct ValidationTypePool {
328 pub string_validators: HashMap<&'static str, Arc<ValidatorDef>>,
330 pub numeric_validators: HashMap<&'static str, Arc<ValidatorDef>>,
332}
333
334#[derive(Debug, Clone)]
336pub struct ValidatorDef {
337 pub name: &'static str,
339 pub validator_type: ValidatorType,
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
345pub enum ValidatorType {
346 StringFormat,
348 StringLength,
350 NumericRange,
352 Pattern,
354 Custom,
356}
357
358impl ValidationTypePool {
359 pub fn new() -> Self {
361 let mut pool = Self::default();
362 pool.init_common_validators();
363 pool
364 }
365
366 fn init_common_validators(&mut self) {
367 let string_formats = [
369 "email", "url", "uuid", "cuid", "cuid2", "nanoid", "ulid",
370 "ipv4", "ipv6", "date", "datetime", "time",
371 ];
372
373 for name in string_formats {
374 self.string_validators.insert(name, Arc::new(ValidatorDef {
375 name,
376 validator_type: ValidatorType::StringFormat,
377 }));
378 }
379
380 let numeric_validators = ["min", "max", "positive", "negative", "nonNegative", "nonPositive"];
382 for name in numeric_validators {
383 self.numeric_validators.insert(name, Arc::new(ValidatorDef {
384 name,
385 validator_type: ValidatorType::NumericRange,
386 }));
387 }
388 }
389
390 pub fn get_string_validator(&self, name: &str) -> Option<&Arc<ValidatorDef>> {
392 self.string_validators.get(name)
393 }
394
395 pub fn get_numeric_validator(&self, name: &str) -> Option<&Arc<ValidatorDef>> {
397 self.numeric_validators.get(name)
398 }
399}
400
401pub static VALIDATION_POOL: std::sync::LazyLock<ValidationTypePool> =
403 std::sync::LazyLock::new(ValidationTypePool::new);
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408
409 #[test]
410 fn test_schema_cache_hit() {
411 let cache = SchemaCache::new();
412
413 let schema1 = cache.get_or_parse("model User { id Int @id }").unwrap();
414 let schema2 = cache.get_or_parse("model User { id Int @id }").unwrap();
415
416 assert!(Arc::ptr_eq(&schema1, &schema2));
418
419 let stats = cache.stats();
420 assert_eq!(stats.hits, 1);
421 assert_eq!(stats.misses, 1);
422 }
423
424 #[test]
425 fn test_schema_cache_miss() {
426 let cache = SchemaCache::new();
427
428 let _ = cache.get_or_parse("model User { id Int @id }").unwrap();
429 let _ = cache.get_or_parse("model Post { id Int @id }").unwrap();
430
431 let stats = cache.stats();
432 assert_eq!(stats.hits, 0);
433 assert_eq!(stats.misses, 2);
434 }
435
436 #[test]
437 fn test_schema_cache_clear() {
438 let cache = SchemaCache::new();
439
440 let _ = cache.get_or_parse("model User { id Int @id }").unwrap();
441 assert_eq!(cache.len(), 1);
442
443 cache.clear();
444 assert_eq!(cache.len(), 0);
445 }
446
447 #[test]
448 fn test_doc_string_interning() {
449 let doc1 = DocString::intern("User profile information");
450 let doc2 = DocString::intern("User profile information");
451
452 assert!(Arc::ptr_eq(doc1.as_arc(), doc2.as_arc()));
454 }
455
456 #[test]
457 fn test_doc_string_different() {
458 let doc1 = DocString::intern("User profile");
459 let doc2 = DocString::intern("Post content");
460
461 assert_ne!(doc1.as_str(), doc2.as_str());
462 }
463
464 #[test]
465 fn test_lazy_field_attrs() {
466 let lazy = LazyFieldAttrs::new();
467
468 assert!(!lazy.is_computed());
469
470 let attrs = lazy.get_or_init(|| FieldAttrsCache {
471 is_id: true,
472 is_auto: true,
473 ..Default::default()
474 });
475
476 assert!(lazy.is_computed());
477 assert!(attrs.is_id);
478 assert!(attrs.is_auto);
479 }
480
481 #[test]
482 fn test_validation_pool() {
483 let pool = ValidationTypePool::new();
484
485 assert!(pool.get_string_validator("email").is_some());
486 assert!(pool.get_string_validator("url").is_some());
487 assert!(pool.get_numeric_validator("min").is_some());
488 assert!(pool.get_numeric_validator("max").is_some());
489 }
490
491 #[test]
492 fn test_cache_stats_hit_rate() {
493 let stats = CacheStats {
494 hits: 8,
495 misses: 2,
496 cached_count: 5,
497 };
498
499 assert!((stats.hit_rate() - 0.8).abs() < 0.001);
500 }
501
502 #[test]
503 fn test_cache_stats_zero() {
504 let stats = CacheStats::default();
505 assert_eq!(stats.hit_rate(), 0.0);
506 }
507}
508