1use std::collections::HashMap;
7use std::hash::Hash;
8use std::sync::{Arc, Mutex, RwLock};
9use std::time::{Duration, Instant};
10
11use crate::component::{Component, ComponentError, ComponentId, Context, Node, StateChanges};
12
13pub trait Memoizable {
15 type MemoKey: Hash + Eq + Clone;
16
17 fn memo_key(&self) -> Self::MemoKey;
19
20 fn should_memo_update(&self, old_key: &Self::MemoKey, new_key: &Self::MemoKey) -> bool {
22 old_key != new_key
23 }
24}
25
26pub struct MemoCache<K, V> {
28 cache: RwLock<HashMap<K, CacheEntry<V>>>,
29 max_size: usize,
30 ttl: Duration,
31}
32
33#[derive(Clone)]
34struct CacheEntry<V> {
35 value: V,
36 created_at: Instant,
37 access_count: u64,
38}
39
40impl<K, V> MemoCache<K, V>
41where
42 K: Hash + Eq + Clone,
43 V: Clone,
44{
45 pub fn new(max_size: usize, ttl: Duration) -> Self {
46 Self {
47 cache: RwLock::new(HashMap::new()),
48 max_size,
49 ttl,
50 }
51 }
52
53 pub fn get(&self, key: &K) -> Option<V> {
54 let mut cache = self.cache.write().ok()?;
55
56 if let Some(entry) = cache.get_mut(key) {
57 if entry.created_at.elapsed() < self.ttl {
59 entry.access_count += 1;
60 return Some(entry.value.clone());
61 } else {
62 cache.remove(key);
64 }
65 }
66 None
67 }
68
69 pub fn set(&self, key: K, value: V) {
70 if let Ok(mut cache) = self.cache.write() {
71 if cache.len() >= self.max_size {
73 self.evict_lru(&mut cache);
74 }
75
76 cache.insert(
77 key,
78 CacheEntry {
79 value,
80 created_at: Instant::now(),
81 access_count: 1,
82 },
83 );
84 }
85 }
86
87 fn evict_lru(&self, cache: &mut HashMap<K, CacheEntry<V>>) {
88 if let Some((key_to_remove, _)) = cache
89 .iter()
90 .min_by_key(|(_, entry)| entry.access_count)
91 .map(|(k, v)| (k.clone(), v.clone()))
92 {
93 cache.remove(&key_to_remove);
94 }
95 }
96
97 pub fn clear(&self) {
98 if let Ok(mut cache) = self.cache.write() {
99 cache.clear();
100 }
101 }
102
103 pub fn size(&self) -> usize {
104 self.cache.read().map(|c| c.len()).unwrap_or(0)
105 }
106}
107
108pub struct MemoComponent<T>
110where
111 T: Component + Memoizable,
112{
113 component: T,
114 last_memo_key: Option<T::MemoKey>,
115 #[allow(dead_code)]
116 cached_render: Option<Vec<Node>>,
117 cache: Arc<MemoCache<T::MemoKey, Vec<Node>>>,
118}
119
120impl<T> MemoComponent<T>
121where
122 T: Component + Memoizable,
123 T::MemoKey: Send + Sync + 'static,
124{
125 pub fn new(component: T) -> Self {
126 Self {
127 component,
128 last_memo_key: None,
129 cached_render: None,
130 cache: Arc::new(MemoCache::new(100, Duration::from_secs(300))), }
132 }
133
134 pub fn with_cache(component: T, cache: Arc<MemoCache<T::MemoKey, Vec<Node>>>) -> Self {
135 Self {
136 component,
137 last_memo_key: None,
138 cached_render: None,
139 cache,
140 }
141 }
142}
143
144impl<T> Component for MemoComponent<T>
145where
146 T: Component + Memoizable + Send + Sync + 'static,
147 T::Props: Send + Sync + 'static,
148 T::MemoKey: Send + Sync + 'static,
149{
150 type Props = T::Props;
151 fn component_id(&self) -> ComponentId {
152 Component::component_id(&self.component)
153 }
154
155 fn create(props: Self::Props, context: Context) -> Self {
156 Self::new(T::create(props, context))
157 }
158
159 fn update(&mut self, props: Self::Props) -> Result<(), ComponentError> {
160 self.component.update(props)
161 }
162
163 fn should_update(&self, new_props: &Self::Props) -> bool {
164 self.component.should_update(new_props)
165 }
166
167 fn render(&self) -> Result<Vec<Node>, ComponentError> {
168 let current_key = self.component.memo_key();
169
170 if let Some(ref last_key) = self.last_memo_key {
172 if !self.component.should_memo_update(last_key, ¤t_key) {
173 if let Some(cached) = self.cache.get(¤t_key) {
174 return Ok(cached);
175 }
176 }
177 }
178
179 let result = self.component.render()?;
181 self.cache.set(current_key, result.clone());
182 Ok(result)
183 }
184
185 fn as_any(&self) -> &dyn std::any::Any {
186 self
187 }
188
189 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
190 self
191 }
192}
193
194pub struct PerformanceMonitor {
196 render_times: RwLock<HashMap<ComponentId, Vec<Duration>>>,
197 update_times: RwLock<HashMap<ComponentId, Vec<Duration>>>,
198 mount_times: RwLock<HashMap<ComponentId, Duration>>,
199}
200
201impl Clone for PerformanceMonitor {
202 fn clone(&self) -> Self {
203 Self::new() }
205}
206
207impl PerformanceMonitor {
208 pub fn new() -> Self {
209 Self {
210 render_times: RwLock::new(HashMap::new()),
211 update_times: RwLock::new(HashMap::new()),
212 mount_times: RwLock::new(HashMap::new()),
213 }
214 }
215
216 pub fn start_render_timing(&self, component_id: ComponentId) -> RenderTimer {
217 RenderTimer::new(component_id, Arc::new(self.clone()))
218 }
219
220 pub fn record_render_time(&self, component_id: ComponentId, duration: Duration) {
221 if let Ok(mut times) = self.render_times.write() {
222 times
223 .entry(component_id)
224 .or_insert_with(Vec::new)
225 .push(duration);
226
227 if let Some(component_times) = times.get_mut(&component_id) {
229 if component_times.len() > 100 {
230 component_times.remove(0);
231 }
232 }
233 }
234 }
235
236 pub fn record_update_time(&self, component_id: ComponentId, duration: Duration) {
237 if let Ok(mut times) = self.update_times.write() {
238 times
239 .entry(component_id)
240 .or_insert_with(Vec::new)
241 .push(duration);
242
243 if let Some(component_times) = times.get_mut(&component_id) {
245 if component_times.len() > 100 {
246 component_times.remove(0);
247 }
248 }
249 }
250 }
251
252 pub fn record_mount_time(&self, component_id: ComponentId, duration: Duration) {
253 if let Ok(mut times) = self.mount_times.write() {
254 times.insert(component_id, duration);
255 }
256 }
257
258 pub fn get_average_render_time(&self, component_id: ComponentId) -> Option<Duration> {
259 if let Ok(times) = self.render_times.read() {
260 if let Some(component_times) = times.get(&component_id) {
261 if !component_times.is_empty() {
262 let total: Duration = component_times.iter().sum();
263 return Some(total / component_times.len() as u32);
264 }
265 }
266 }
267 None
268 }
269
270 pub fn get_render_statistics(&self, component_id: ComponentId) -> RenderStatistics {
271 if let Ok(times) = self.render_times.read() {
272 if let Some(component_times) = times.get(&component_id) {
273 if !component_times.is_empty() {
274 let total: Duration = component_times.iter().sum();
275 let average = total / component_times.len() as u32;
276 let min = *component_times.iter().min().unwrap();
277 let max = *component_times.iter().max().unwrap();
278
279 return RenderStatistics {
280 count: component_times.len(),
281 total,
282 average,
283 min,
284 max,
285 };
286 }
287 }
288 }
289 RenderStatistics::default()
290 }
291}
292
293impl Default for PerformanceMonitor {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299#[derive(Debug, Clone)]
300pub struct RenderStatistics {
301 pub count: usize,
302 pub total: Duration,
303 pub average: Duration,
304 pub min: Duration,
305 pub max: Duration,
306}
307
308impl Default for RenderStatistics {
309 fn default() -> Self {
310 Self {
311 count: 0,
312 total: Duration::ZERO,
313 average: Duration::ZERO,
314 min: Duration::ZERO,
315 max: Duration::ZERO,
316 }
317 }
318}
319
320pub struct RenderTimer {
322 component_id: ComponentId,
323 start_time: Instant,
324 monitor: Arc<PerformanceMonitor>,
325}
326
327impl RenderTimer {
328 fn new(component_id: ComponentId, monitor: Arc<PerformanceMonitor>) -> Self {
329 Self {
330 component_id,
331 start_time: Instant::now(),
332 monitor,
333 }
334 }
335}
336
337impl Drop for RenderTimer {
338 fn drop(&mut self) {
339 let duration = self.start_time.elapsed();
340 self.monitor.record_render_time(self.component_id, duration);
341 }
342}
343
344pub struct UpdateBatcher {
346 pending_updates: Mutex<HashMap<ComponentId, Vec<StateChanges>>>,
347 batch_timeout: Duration,
348 max_batch_size: usize,
349}
350
351impl UpdateBatcher {
352 pub fn new(batch_timeout: Duration, max_batch_size: usize) -> Self {
353 Self {
354 pending_updates: Mutex::new(HashMap::new()),
355 batch_timeout,
356 max_batch_size,
357 }
358 }
359
360 pub fn queue_update(&self, component_id: ComponentId, changes: StateChanges) {
361 if let Ok(mut pending) = self.pending_updates.lock() {
362 pending
363 .entry(component_id)
364 .or_insert_with(Vec::new)
365 .push(changes);
366 }
367 }
368
369 pub fn flush_updates(&self) -> HashMap<ComponentId, Vec<StateChanges>> {
370 if let Ok(mut pending) = self.pending_updates.lock() {
371 let updates = pending.clone();
372 pending.clear();
373 updates
374 } else {
375 HashMap::new()
376 }
377 }
378
379 pub fn should_flush(&self, component_id: ComponentId) -> bool {
380 if let Ok(pending) = self.pending_updates.lock() {
381 if let Some(updates) = pending.get(&component_id) {
382 return updates.len() >= self.max_batch_size
383 || updates
384 .first()
385 .map(|u| u.batch_timestamp.elapsed() >= self.batch_timeout)
386 .unwrap_or(false);
387 }
388 }
389 false
390 }
391}
392
393pub struct LazyComponent<T>
395where
396 T: Component,
397{
398 component: Option<T>,
399 props: Option<T::Props>,
400 context: Context,
401 loaded: bool,
402 load_trigger: LoadTrigger,
403}
404
405#[derive(Clone)]
406pub enum LoadTrigger {
407 Immediate,
408 OnMount,
409 OnFirstRender,
410 OnVisible,
411}
412
413impl<T> LazyComponent<T>
414where
415 T: Component,
416{
417 pub fn new(context: Context, load_trigger: LoadTrigger) -> Self {
418 Self {
419 component: None,
420 props: None,
421 context,
422 loaded: false,
423 load_trigger,
424 }
425 }
426
427 fn ensure_loaded(&mut self) -> Result<(), ComponentError> {
428 if !self.loaded {
429 if let Some(props) = self.props.clone() {
430 self.component = Some(T::create(props, self.context.clone()));
431 self.loaded = true;
432 }
433 }
434 Ok(())
435 }
436}
437
438impl<T> Component for LazyComponent<T>
439where
440 T: Component + Send + Sync + 'static,
441 T::Props: Send + Sync + 'static,
442{
443 type Props = T::Props;
444 fn component_id(&self) -> ComponentId {
445 if let Some(ref component) = self.component {
446 Component::component_id(component)
447 } else {
448 ComponentId::new() }
450 }
451
452 fn create(props: Self::Props, context: Context) -> Self {
453 let mut lazy = Self::new(context, LoadTrigger::OnMount);
454 lazy.props = Some(props);
455 lazy
456 }
457
458 fn mount(&mut self) -> Result<(), ComponentError> {
459 if matches!(
460 self.load_trigger,
461 LoadTrigger::OnMount | LoadTrigger::Immediate
462 ) {
463 self.ensure_loaded()?;
464 if let Some(ref mut component) = self.component {
465 component.mount()?;
466 }
467 }
468 Ok(())
469 }
470
471 fn update(&mut self, props: Self::Props) -> Result<(), ComponentError> {
472 self.props = Some(props.clone());
473 if let Some(ref mut component) = self.component {
474 component.update(props)?;
475 }
476 Ok(())
477 }
478
479 fn render(&self) -> Result<Vec<Node>, ComponentError> {
480 if let Some(ref component) = self.component {
481 component.render()
482 } else {
483 Ok(Vec::new())
485 }
486 }
487
488 fn as_any(&self) -> &dyn std::any::Any {
489 self
490 }
491
492 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
493 self
494 }
495}
496
497pub struct PerformanceRegistry {
499 monitor: Arc<PerformanceMonitor>,
500 memo_cache: Arc<MemoCache<String, Vec<Node>>>,
501 update_batcher: Arc<UpdateBatcher>,
502}
503
504impl PerformanceRegistry {
505 pub fn new() -> Self {
506 Self {
507 monitor: Arc::new(PerformanceMonitor::new()),
508 memo_cache: Arc::new(MemoCache::new(1000, Duration::from_secs(600))),
509 update_batcher: Arc::new(UpdateBatcher::new(
510 Duration::from_millis(16), 10, )),
513 }
514 }
515
516 pub fn monitor(&self) -> Arc<PerformanceMonitor> {
517 self.monitor.clone()
518 }
519
520 pub fn memo_cache(&self) -> Arc<MemoCache<String, Vec<Node>>> {
521 self.memo_cache.clone()
522 }
523
524 pub fn update_batcher(&self) -> Arc<UpdateBatcher> {
525 self.update_batcher.clone()
526 }
527}
528
529impl Default for PerformanceRegistry {
530 fn default() -> Self {
531 Self::new()
532 }
533}
534
535#[macro_export]
539macro_rules! memo {
540 ($component:expr) => {
541 MemoComponent::new($component)
542 };
543 ($component:expr, $cache:expr) => {
544 MemoComponent::with_cache($component, $cache)
545 };
546}
547
548#[macro_export]
550macro_rules! lazy {
551 ($component_type:ty, $context:expr) => {
552 LazyComponent::<$component_type>::new($context, LoadTrigger::OnMount)
553 };
554 ($component_type:ty, $context:expr, $trigger:expr) => {
555 LazyComponent::<$component_type>::new($context, $trigger)
556 };
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562 use crate::component::ComponentBase;
563
564 #[derive(Clone, Hash, PartialEq, Eq)]
565 struct TestMemoKey {
566 id: u64,
567 version: u32,
568 }
569
570 #[derive(Clone)]
571 struct TestProps {
572 id: u64,
573 version: u32,
574 }
575 struct TestComponent {
578 base: ComponentBase,
579 props: TestProps,
580 }
581
582 impl Component for TestComponent {
583 type Props = TestProps;
584
585 fn component_id(&self) -> ComponentId {
586 self.base.id()
587 }
588
589 fn create(props: Self::Props, context: Context) -> Self {
590 Self {
591 base: ComponentBase::new(context),
592 props,
593 }
594 }
595
596 fn update(&mut self, props: Self::Props) -> Result<(), ComponentError> {
597 self.props = props;
598 Ok(())
599 }
600
601 fn render(&self) -> Result<Vec<Node>, ComponentError> {
602 Ok(vec![])
603 }
604
605 fn as_any(&self) -> &dyn std::any::Any {
606 self
607 }
608
609 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
610 self
611 }
612 }
613
614 impl Memoizable for TestComponent {
615 type MemoKey = TestMemoKey;
616
617 fn memo_key(&self) -> Self::MemoKey {
618 TestMemoKey {
619 id: self.props.id,
620 version: self.props.version,
621 }
622 }
623 }
624
625 #[test]
626 fn test_memo_cache() {
627 let cache: MemoCache<String, Vec<Node>> = MemoCache::new(2, Duration::from_secs(1));
628
629 cache.set("key1".to_string(), vec![]);
630 cache.set("key2".to_string(), vec![]);
631 assert_eq!(cache.size(), 2);
632
633 cache.set("key3".to_string(), vec![]);
635 assert_eq!(cache.size(), 2);
636 }
637
638 #[test]
639 fn test_performance_monitor() {
640 let monitor = PerformanceMonitor::new();
641 let component_id = ComponentId::new();
642
643 monitor.record_render_time(component_id, Duration::from_millis(10));
644 monitor.record_render_time(component_id, Duration::from_millis(20));
645
646 let avg = monitor.get_average_render_time(component_id).unwrap();
647 assert_eq!(avg, Duration::from_millis(15));
648 }
649
650 #[test]
651 fn test_update_batcher() {
652 let batcher = UpdateBatcher::new(Duration::from_millis(100), 5);
653 let component_id = ComponentId::new();
654
655 let changes = StateChanges {
656 changes: vec![],
657 batch_timestamp: std::time::Instant::now(),
658 immediate: false,
659 };
660
661 batcher.queue_update(component_id, changes);
662 let updates = batcher.flush_updates();
663 assert!(updates.contains_key(&component_id));
664 }
665
666 #[test]
667 fn test_memoized_component() {
668 let context = Context::new();
669 let props = TestProps { id: 1, version: 1 };
670 let component = TestComponent::create(props, context);
671 let memo_component = MemoComponent::new(component);
672
673 assert!(memo_component.render().is_ok());
674 }
675}