1use std::sync::OnceLock;
5use tracing_indicatif::IndicatifLayer;
6use tracing_subscriber::layer::SubscriberExt;
7use tracing_subscriber::util::SubscriberInitExt;
8
9#[allow(dead_code)]
11static INDICATIF_LAYER: OnceLock<IndicatifLayer<tracing_subscriber::Registry>> = OnceLock::new();
12
13pub fn setup_tracing() {
15 init_tracing(false);
16}
17
18pub fn init_tracing(verbose: bool) {
21 use std::sync::Once;
22 static INIT: Once = Once::new();
23
24 INIT.call_once(|| {
25 let indicatif_layer = IndicatifLayer::new();
26
27 let env_filter = if verbose {
31 tracing_subscriber::EnvFilter::new("vx=debug,info")
32 } else {
33 tracing_subscriber::EnvFilter::new("vx=info,warn,error")
34 };
35
36 tracing_subscriber::registry()
37 .with(env_filter)
38 .with(
39 tracing_subscriber::fmt::layer()
40 .with_writer(indicatif_layer.get_stderr_writer())
41 .with_target(false)
42 .with_level(verbose),
43 )
44 .with(indicatif_layer)
45 .try_init()
46 .ok(); });
48}
49
50pub fn get_indicatif_layer() -> Option<()> {
53 None
55}
56
57#[macro_export]
60macro_rules! progress_span {
61 ($name:expr, $($field:tt)*) => {
62 tracing::info_span!($name, $($field)*)
63 };
64}
65
66#[macro_export]
69macro_rules! with_progress_span {
70 ($name:expr, $operation:expr) => {{
71 let span = tracing::info_span!($name);
72 async move {
73 let _enter = span.enter();
74 $operation.await
75 }
76 }};
77}
78
79#[macro_export]
81macro_rules! with_progress_events {
82 ($name:expr, $success_msg:expr, $error_msg:expr, $operation:expr) => {{
83 let span = tracing::info_span!($name);
84 async move {
85 let _enter = span.enter();
86 match $operation.await {
87 Ok(result) => {
88 tracing::info!($success_msg);
89 Ok(result)
90 }
91 Err(error) => {
92 tracing::error!("{}: {}", $error_msg, error);
93 Err(error)
94 }
95 }
96 }
97 }};
98}
99
100pub fn create_manual_progress_bar(len: u64, message: &str) -> indicatif::ProgressBar {
102 use indicatif::{ProgressBar, ProgressStyle};
103
104 let pb = ProgressBar::new(len);
105 pb.set_style(
106 ProgressStyle::with_template(
107 "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} {msg}",
108 )
109 .unwrap()
110 .progress_chars("#>-"),
111 );
112 pb.set_message(message.to_string());
113
114 if let Some(_layer) = get_indicatif_layer() {
116 }
118
119 pb
120}
121
122pub fn suspend_progress_bars<F, R>(f: F) -> R
124where
125 F: FnOnce() -> R,
126{
127 if let Some(_layer) = get_indicatif_layer() {
128 tracing_indicatif::suspend_tracing_indicatif(f)
129 } else {
130 f()
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[tokio::test]
139 async fn test_progress_span_macro() {
140 init_tracing(true);
141
142 let result = with_progress_span!("test_operation", async {
143 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
145 Ok::<_, anyhow::Error>(42)
146 })
147 .await;
148
149 assert!(result.is_ok());
150 assert_eq!(result.unwrap(), 42);
151 }
152
153 #[tokio::test]
154 async fn test_progress_events_macro() {
155 init_tracing(true);
156
157 let result = with_progress_events!(
158 "test_operation_with_events",
159 "Operation completed successfully",
160 "Operation failed",
161 async {
162 tokio::time::sleep(std::time::Duration::from_millis(5)).await;
164 Ok::<_, anyhow::Error>("success")
165 }
166 )
167 .await;
168
169 assert!(result.is_ok());
170 assert_eq!(result.unwrap(), "success");
171 }
172}