Skip to main content

v_utils/utils/
mod.rs

1pub mod eyre;
2pub use eyre::*;
3
4pub mod format;
5pub use format::*;
6
7pub mod info_size;
8pub use info_size::*;
9
10pub mod serde;
11pub use serde::*;
12
13/// Macro for logging to both stdout and tracing info
14/// Usage: log!("message") or log!("format {}", arg)
15#[macro_export]
16macro_rules! log {
17	($($arg:tt)*) => {{
18		println!($($arg)*);
19		tracing::info!($($arg)*);
20	}};
21}
22
23/// Prints to stderr, overwriting the current line in-place (like cargo's progress bar).
24/// Uses `\r` + ANSI clear-line so the output stays on a single terminal line.
25/// Usage: print_rolling!("status: {}", value)
26#[macro_export]
27macro_rules! print_rolling {
28	($($arg:tt)*) => {{
29		eprint!("\r\x1B[2K");
30		eprint!($($arg)*);
31	}};
32}
33
34/// Macro for logging to both stderr and tracing debug
35/// Usage: elog!("message") or elog!("format {}", arg)
36#[macro_export]
37macro_rules! elog {
38	($($arg:tt)*) => {{
39		eprintln!($($arg)*);
40		tracing::debug!($($arg)*);
41	}};
42}
43
44#[cfg(feature = "tracing")]
45pub mod tracing;
46#[cfg(feature = "tracing")]
47pub use tracing::*;
48
49/// Sets up error handling (color_eyre, miette) and tracing subscriber for client-side applications.
50///
51/// HACK: Assumes that `color_eyre` and `miette` are in scope.
52///
53/// # Behavior
54/// - Installs color_eyre panic/error hooks (with span trace capture disabled)
55/// - Installs miette diagnostic hook (with terminal links and 3 context lines)
56/// - Initializes tracing subscriber with:
57///   - JSON formatted logs to `~/.local/state/{pkg_name}/{pkg_name}.log`
58///   - WARN and ERROR also printed to stderr
59///   - Log directives from `LOG_DIRECTIVES` env var (set by build.rs)
60///
61/// # Integration Test Mode
62/// When `__IS_INTEGRATION_TEST` env var is set, logs to stdout with debug level instead.
63/// This allows integration tests to capture and inspect log output.
64///
65/// # Usage
66/// ```ignore
67/// // Default log filename ({pkg_name}.log)
68/// v_utils::clientside!();
69///
70/// // Custom log filename from Option<String> (e.g., from CLI arg)
71/// v_utils::clientside!(extract_log_to());
72/// ```
73#[cfg(all(feature = "tracing", feature = "xdg"))]
74#[macro_export]
75macro_rules! clientside {
76	() => {
77		v_utils::clientside!(None::<String>);
78	};
79	($fname:expr) => {
80		//color_eyre::config::HookBuilder::default().capture_span_trace_by_default(false).install().unwrap(); // thought would allow for nice interop with miette, but in reality I lose my colored traces
81		color_eyre::install().unwrap();
82		miette::set_hook(Box::new(|_| Box::new(miette::MietteHandlerOpts::new().terminal_links(true).build()))).expect("miette hook already set");
83		if std::env::var("__IS_INTEGRATION_TEST").is_ok() {
84			// SAFETY: Called at program start before any other threads are spawned
85			unsafe { std::env::set_var("LOG_DIRECTIVES", concat!("debug,", env!("CARGO_PKG_NAME"), "=debug")) };
86			v_utils::utils::init_subscriber(v_utils::utils::LogDestination::default());
87		} else {
88			let mut dest = v_utils::utils::LogDestination::xdg(env!("CARGO_PKG_NAME"))
89				.stderr_errors(true)
90				.compiled_directives(option_env!("LOG_DIRECTIVES"));
91			if let Some(fname) = $fname {
92				dest = dest.fname(fname);
93			}
94			v_utils::utils::init_subscriber(dest);
95		}
96	};
97}
98
99/// Fallback when xdg is not available - logs to stdout
100#[cfg(all(feature = "tracing", not(feature = "xdg")))]
101#[macro_export]
102macro_rules! clientside {
103	() => {
104		v_utils::clientside!(None::<String>);
105	};
106	($fname:expr) => {
107		let _ = $fname; // silence unused warning
108		eprintln!("[v_utils] Warning: `xdg` feature not enabled, logging to stdout instead of file. Add `xdg` feature to v_utils dependency to enable file logging.");
109		color_eyre::install().unwrap();
110		miette::set_hook(Box::new(|_| Box::new(miette::MietteHandlerOpts::new().terminal_links(true).context_lines(3).build()))).expect("miette hook already set");
111		v_utils::utils::init_subscriber(v_utils::utils::LogDestination::default().compiled_directives(option_env!("LOG_DIRECTIVES")));
112	};
113}
114
115/// **Warning**: Consider using `strum` crate instead - this macro is likely redundant for most use cases.
116#[macro_export]
117macro_rules! define_str_enum {
118  ($(#[$meta:meta])* $vis:vis enum $name:ident {
119    $($(#[$variant_meta:meta])* $variant:ident => $str:expr),* $(,)?
120  }) => {
121    $(#[$meta])*
122    $vis enum $name {
123      $($(#[$variant_meta])* $variant),*
124    }
125
126    impl std::fmt::Display for $name {
127      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        match self {
129          $(Self::$variant => write!(f, "{}", $str)),*
130        }
131      }
132    }
133
134    impl std::str::FromStr for $name {
135      type Err = eyre::Report;
136
137      fn from_str(s: &str) -> Result<Self, Self::Err> {
138        match s {
139          $($str => Ok(Self::$variant)),*,
140          _ => eyre::bail!("Invalid {} string: {}", stringify!($name).to_lowercase(), s),
141        }
142      }
143    }
144  };
145}