1#[cfg(feature = "full")]
54pub mod category;
55#[cfg(feature = "full")]
56pub mod collector;
57#[cfg(feature = "full")]
58pub mod metrics;
59#[cfg(feature = "full")]
60pub mod reporter;
61#[cfg(feature = "full")]
62pub mod timer;
63
64#[cfg(not(feature = "full"))]
68pub mod category {
69 pub trait Category: Clone + Eq + std::hash::Hash + Send + Sync + 'static {
70 fn description(&self) -> Option<&str> {
71 None
72 }
73 fn color_hint(&self) -> Option<&str> {
74 None
75 }
76 fn priority(&self) -> i32 {
77 0
78 }
79 }
80
81 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
82 pub enum DefaultCategory {
83 IO,
84 Compute,
85 Network,
86 Database,
87 Other,
88 }
89
90 impl Category for DefaultCategory {}
91}
92
93#[cfg(not(feature = "full"))]
94pub mod collector {
95 use super::category::Category;
96 use std::collections::HashMap;
97
98 #[derive(Debug, Clone, Default)]
99 pub struct OperationStats {
100 pub count: u64,
101 pub total_micros: u64,
102 pub min_micros: u64,
103 pub max_micros: u64,
104 pub mean_micros: u64,
105 }
106
107 pub struct ProfileCollector;
108
109 impl ProfileCollector {
110 pub fn record(_operation: &str, _duration_micros: u64) {}
111 pub fn record_with_category<C: Category>(
112 _operation: &str,
113 _category: C,
114 _duration_micros: u64,
115 ) {
116 }
117 pub fn get_stats(_operation: &str) -> Option<OperationStats> {
118 None
119 }
120 pub fn get_all_stats() -> HashMap<String, OperationStats> {
121 HashMap::new()
122 }
123 pub fn get_category<C: Category>(_operation: &str) -> Option<C> {
124 None
125 }
126 pub fn clear_all() {}
127 pub fn reset_all() {}
128 pub fn reset_operation(_operation: &str) {}
129 pub fn has_data() -> bool {
130 false
131 }
132 pub fn total_operations() -> u64 {
133 0
134 }
135 pub fn get_summary() -> SummaryStats {
136 SummaryStats::default()
137 }
138 pub fn get_stats_by_category<C: Category>() -> HashMap<C, Vec<(String, OperationStats)>> {
139 HashMap::new()
140 }
141 }
142
143 #[derive(Debug, Default)]
144 pub struct SummaryStats {
145 pub total_operations: u64,
146 pub unique_operations: usize,
147 pub total_time_micros: u64,
148 }
149}
150
151#[cfg(not(feature = "full"))]
152pub mod reporter {
153 use super::category::Category;
154 use super::collector::OperationStats;
155 use std::collections::HashMap;
156
157 #[derive(Debug, Clone, Copy)]
158 pub enum TimeFormat {
159 Microseconds,
160 Milliseconds,
161 Seconds,
162 Auto,
163 }
164
165 #[derive(Debug, Clone, Copy)]
166 pub enum SortMetric {
167 TotalTime,
168 MeanTime,
169 MaxTime,
170 CallCount,
171 }
172
173 #[derive(Debug, Clone, Copy)]
174 pub struct Percentile {
175 pub p50: u64,
176 pub p95: u64,
177 pub p99: u64,
178 pub p999: u64,
179 }
180
181 #[derive(Debug)]
182 pub struct ReportConfig {
183 pub include_percentiles: bool,
184 pub group_by_category: bool,
185 pub time_format: TimeFormat,
186 pub sort_by: SortMetric,
187 pub sort_by_time: bool,
188 pub min_samples: u64,
189 }
190
191 impl Default for ReportConfig {
192 fn default() -> Self {
193 Self {
194 include_percentiles: false,
195 group_by_category: false,
196 time_format: TimeFormat::Auto,
197 sort_by: SortMetric::TotalTime,
198 sort_by_time: false,
199 min_samples: 0,
200 }
201 }
202 }
203
204 pub struct ProfileReport<C: Category> {
205 pub stats: HashMap<String, OperationStats>,
206 pub categories: HashMap<String, C>,
207 pub config: ReportConfig,
208 }
209
210 impl<C: Category> ProfileReport<C> {
211 pub fn generate() -> Self {
212 Self {
213 stats: HashMap::new(),
214 categories: HashMap::new(),
215 config: ReportConfig::default(),
216 }
217 }
218
219 pub fn generate_with_config(_config: ReportConfig) -> Self {
220 Self {
221 stats: HashMap::new(),
222 categories: HashMap::new(),
223 config: ReportConfig::default(),
224 }
225 }
226
227 pub fn quick_summary(&self) -> String {
228 String::new()
229 }
230
231 pub fn summary_stats(&self) -> super::collector::SummaryStats {
232 super::collector::SummaryStats::default()
233 }
234
235 pub fn to_string(&self) -> String {
236 String::new()
237 }
238
239 pub fn top_operations_by(
240 &self,
241 _metric: SortMetric,
242 _limit: usize,
243 ) -> Vec<(String, OperationStats)> {
244 Vec::new()
245 }
246 }
247
248 impl<C: Category> std::fmt::Debug for ProfileReport<C> {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 write!(f, "")
251 }
252 }
253
254 pub struct ReportBuilder<C: Category> {
255 _phantom: std::marker::PhantomData<C>,
256 }
257
258 impl<C: Category> Default for ReportBuilder<C> {
259 fn default() -> Self {
260 Self::new()
261 }
262 }
263
264 impl<C: Category> ReportBuilder<C> {
265 pub fn new() -> Self {
266 Self {
267 _phantom: std::marker::PhantomData,
268 }
269 }
270
271 pub fn group_by_category(self, _enabled: bool) -> Self {
272 self
273 }
274 pub fn include_percentiles(self, _enabled: bool) -> Self {
275 self
276 }
277 pub fn time_format(self, _format: TimeFormat) -> Self {
278 self
279 }
280 pub fn sort_by_time(self, _enabled: bool) -> Self {
281 self
282 }
283 pub fn build(self) -> ProfileReport<C> {
284 ProfileReport::generate()
285 }
286 }
287}
288
289#[cfg(not(feature = "full"))]
290pub mod timer {
291 use super::category::Category;
292
293 pub struct ProfileTimer;
294
295 impl ProfileTimer {
296 pub fn new(_operation: &str) -> Self {
297 ProfileTimer
298 }
299
300 pub fn with_category<C: Category>(_operation: &str, _category: C) -> Self {
301 ProfileTimer
302 }
303 }
304
305 impl Drop for ProfileTimer {
306 fn drop(&mut self) {
307 }
309 }
310
311 pub struct PausableTimer;
312}
313
314pub use category::{Category, DefaultCategory};
316pub use collector::{OperationStats, ProfileCollector};
317pub use reporter::{
318 Percentile, ProfileReport, ReportBuilder, ReportConfig, SortMetric, TimeFormat,
319};
320pub use timer::{PausableTimer, ProfileTimer};
321
322#[cfg(not(feature = "full"))]
323pub use collector::SummaryStats;
324#[cfg(feature = "full")]
325pub use collector::SummaryStats;
326
327use std::marker::PhantomData;
328
329pub struct Profiler<C: Category = DefaultCategory> {
346 _phantom: PhantomData<C>,
347}
348
349impl<C: Category> Default for Profiler<C> {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355impl<C: Category> Profiler<C> {
356 pub fn new() -> Self {
358 Self {
359 _phantom: PhantomData,
360 }
361 }
362
363 #[cfg(feature = "full")]
375 pub fn time<T, F>(operation: &str, f: F) -> T
376 where
377 F: FnOnce() -> T,
378 {
379 let _timer = ProfileTimer::new(operation);
380 f()
381 }
382
383 #[cfg(not(feature = "full"))]
384 pub fn time<T, F>(_operation: &str, f: F) -> T
385 where
386 F: FnOnce() -> T,
387 {
388 f()
389 }
390
391 #[cfg(feature = "full")]
393 pub fn time_with_category<T, F>(operation: &str, category: C, f: F) -> T
394 where
395 F: FnOnce() -> T,
396 {
397 let _timer = ProfileTimer::with_category(operation, category);
398 f()
399 }
400
401 #[cfg(not(feature = "full"))]
402 pub fn time_with_category<T, F>(_operation: &str, _category: C, f: F) -> T
403 where
404 F: FnOnce() -> T,
405 {
406 f()
407 }
408
409 #[cfg(feature = "full")]
411 pub async fn time_async<T, F, Fut>(operation: &str, f: F) -> T
412 where
413 F: FnOnce() -> Fut,
414 Fut: std::future::Future<Output = T>,
415 {
416 let _timer = ProfileTimer::new(operation);
417 f().await
418 }
419
420 #[cfg(not(feature = "full"))]
421 pub async fn time_async<T, F, Fut>(_operation: &str, f: F) -> T
422 where
423 F: FnOnce() -> Fut,
424 Fut: std::future::Future<Output = T>,
425 {
426 f().await
427 }
428
429 #[cfg(feature = "full")]
431 pub async fn time_async_with_category<T, F, Fut>(operation: &str, category: C, f: F) -> T
432 where
433 F: FnOnce() -> Fut,
434 Fut: std::future::Future<Output = T>,
435 {
436 let _timer = ProfileTimer::with_category(operation, category);
437 f().await
438 }
439
440 #[cfg(not(feature = "full"))]
441 pub async fn time_async_with_category<T, F, Fut>(_operation: &str, _category: C, f: F) -> T
442 where
443 F: FnOnce() -> Fut,
444 Fut: std::future::Future<Output = T>,
445 {
446 f().await
447 }
448
449 pub fn report() -> ProfileReport<C> {
451 ProfileReport::generate()
452 }
453
454 pub fn report_with_config(config: ReportConfig) -> ProfileReport<C> {
456 ProfileReport::generate_with_config(config)
457 }
458
459 pub fn reset() {
461 ProfileCollector::reset_all()
462 }
463
464 pub fn reset_operation(operation: &str) {
466 ProfileCollector::reset_operation(operation)
467 }
468
469 pub fn record(operation: &str, value_micros: u64) {
471 ProfileCollector::record(operation, value_micros);
472 }
473
474 pub fn record_with_category(operation: &str, category: C, value_micros: u64) {
476 ProfileCollector::record_with_category(operation, category, value_micros);
477 }
478
479 pub fn has_data() -> bool {
481 ProfileCollector::has_data()
482 }
483
484 pub fn total_operations() -> u64 {
486 ProfileCollector::total_operations()
487 }
488
489 pub fn get_stats(operation: &str) -> Option<OperationStats> {
491 ProfileCollector::get_stats(operation)
492 }
493
494 pub fn get_all_stats() -> std::collections::HashMap<String, OperationStats> {
496 ProfileCollector::get_all_stats()
497 }
498}
499
500pub struct ProfilerBuilder<C: Category = DefaultCategory> {
502 config: ReportConfig,
503 _phantom: PhantomData<C>,
504}
505
506impl<C: Category> ProfilerBuilder<C> {
507 pub fn new() -> Self {
509 Self {
510 config: ReportConfig::default(),
511 _phantom: PhantomData,
512 }
513 }
514
515 pub fn with_percentiles(mut self, include: bool) -> Self {
517 self.config.include_percentiles = include;
518 self
519 }
520
521 pub fn sort_by_time(mut self, sort: bool) -> Self {
523 self.config.sort_by_time = sort;
524 self
525 }
526
527 pub fn min_samples(mut self, min: u64) -> Self {
529 self.config.min_samples = min;
530 self
531 }
532
533 pub fn group_by_category(mut self, group: bool) -> Self {
535 self.config.group_by_category = group;
536 self
537 }
538
539 pub fn time_format(mut self, format: TimeFormat) -> Self {
541 self.config.time_format = format;
542 self
543 }
544
545 pub fn build(self) -> Profiler<C> {
547 Profiler::new()
548 }
549}
550
551impl<C: Category> Default for ProfilerBuilder<C> {
552 fn default() -> Self {
553 Self::new()
554 }
555}
556
557#[macro_export]
568macro_rules! profile {
569 ($operation:expr => $block:block) => {{
571 let op_name = format!("{:?}", $operation);
572 $crate::Profiler::time_with_category(&op_name, $operation, || $block)
573 }};
574 ($operation:expr => async $block:block) => {{
575 let op_name = format!("{:?}", $operation);
576 $crate::Profiler::time_async_with_category(&op_name, $operation, || async move $block).await
577 }};
578 ($name:expr, $category:expr => $block:block) => {
580 $crate::Profiler::time_with_category($name, $category, || $block)
581 };
582 ($name:expr, $category:expr => async $block:block) => {
583 $crate::Profiler::time_async_with_category($name, $category, || async move $block).await
584 };
585}
586
587#[macro_export]
589macro_rules! profile_if {
590 ($condition:expr, $operation:expr => $block:block) => {
592 if $condition {
593 $crate::profile!($operation => $block)
594 } else {
595 $block
596 }
597 };
598 ($condition:expr, $name:expr, $category:expr => $block:block) => {
600 if $condition {
601 $crate::profile!($name, $category => $block)
602 } else {
603 $block
604 }
605 };
606}
607
608#[macro_export]
610macro_rules! scoped_timer {
611 ($name:expr) => {
612 let _timer = $crate::ProfileTimer::<$crate::DefaultCategory>::new($name);
613 };
614 ($name:expr, $category:expr) => {
615 let _timer = $crate::ProfileTimer::with_category($name, $category);
616 };
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn test_basic_profiling() {
625 Profiler::<DefaultCategory>::reset();
626
627 let result = Profiler::<DefaultCategory>::time("test_operation", || {
628 std::thread::sleep(std::time::Duration::from_millis(1));
629 42
630 });
631
632 assert_eq!(result, 42);
633 assert!(Profiler::<DefaultCategory>::has_data());
634
635 let stats = Profiler::<DefaultCategory>::get_stats("test_operation");
636 assert!(stats.is_some());
637 assert_eq!(stats.unwrap().count, 1);
638 }
639
640 #[test]
641 fn test_custom_category() {
642 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
643 enum TestCategory {
644 Fast,
645 Slow,
646 }
647
648 impl Category for TestCategory {}
649
650 Profiler::<TestCategory>::reset();
651
652 Profiler::<TestCategory>::record_with_category("op1", TestCategory::Fast, 100);
653 Profiler::<TestCategory>::record_with_category("op2", TestCategory::Slow, 1000);
654
655 assert_eq!(Profiler::<TestCategory>::total_operations(), 2);
656 }
657
658 #[tokio::test]
659 async fn test_async_profiling() {
660 Profiler::<DefaultCategory>::reset();
661
662 let result = Profiler::<DefaultCategory>::time_async("async_test", || async {
663 tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
664 "async_result"
665 })
666 .await;
667
668 assert_eq!(result, "async_result");
669 assert!(Profiler::<DefaultCategory>::has_data());
670 }
671
672 #[test]
673 fn test_profile_macro() {
674 Profiler::<DefaultCategory>::reset();
675
676 let result = profile!("macro_test", DefaultCategory::Other => {
677 std::thread::sleep(std::time::Duration::from_millis(1));
678 100
679 });
680
681 assert_eq!(result, 100);
682 assert!(Profiler::<DefaultCategory>::get_stats("macro_test").is_some());
683 }
684
685 #[test]
686 fn test_conditional_profiling() {
687 Profiler::<DefaultCategory>::reset();
688
689 let should_profile = true;
690 let result = profile_if!(should_profile, "conditional_test", DefaultCategory::Other => {
691 42
692 });
693
694 assert_eq!(result, 42);
695 assert!(Profiler::<DefaultCategory>::get_stats("conditional_test").is_some());
696
697 let should_not_profile = false;
698 let result2 = profile_if!(should_not_profile, "conditional_test2", DefaultCategory::Other => {
699 84
700 });
701
702 assert_eq!(result2, 84);
703 assert!(Profiler::<DefaultCategory>::get_stats("conditional_test2").is_none());
704 }
705}