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