1use std::str::FromStr;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4pub enum Section {
5 FunctionsTiming,
6 FunctionsAlloc,
7 FunctionsCpu,
8 Channels,
9 Streams,
10 Futures,
11 RwLocks,
12 Mutexes,
13 Sql,
14 Http,
15 Threads,
16 Debug,
17}
18
19impl Section {
20 pub fn all() -> Vec<Section> {
21 vec![
22 Section::FunctionsTiming,
23 Section::FunctionsAlloc,
24 Section::FunctionsCpu,
25 Section::Channels,
26 Section::Streams,
27 Section::Futures,
28 Section::RwLocks,
29 Section::Mutexes,
30 Section::Sql,
31 Section::Http,
32 Section::Threads,
33 Section::Debug,
34 ]
35 }
36
37 pub fn short_name(&self) -> &'static str {
38 match self {
39 Section::FunctionsTiming => "timing",
40 Section::FunctionsAlloc => "alloc",
41 Section::FunctionsCpu => "cpu",
42 Section::Channels => "channels",
43 Section::Streams => "streams",
44 Section::Futures => "futures",
45 Section::RwLocks => "rw_locks",
46 Section::Mutexes => "mutexes",
47 Section::Sql => "sql",
48 Section::Http => "http",
49 Section::Threads => "threads",
50 Section::Debug => "debug",
51 }
52 }
53
54 pub fn from_name(s: &str) -> Option<Section> {
55 match s.trim() {
56 "functions-timing" => Some(Section::FunctionsTiming),
57 "functions-alloc" => Some(Section::FunctionsAlloc),
58 "functions-cpu" => Some(Section::FunctionsCpu),
59 "channels" => Some(Section::Channels),
60 "streams" => Some(Section::Streams),
61 "futures" => Some(Section::Futures),
62 "rw_locks" => Some(Section::RwLocks),
63 "mutexes" => Some(Section::Mutexes),
64 "sql" => Some(Section::Sql),
65 "http" => Some(Section::Http),
66 "threads" => Some(Section::Threads),
67 "debug" => Some(Section::Debug),
68 _ => None,
69 }
70 }
71}
72
73#[cfg(feature = "hotpath-meta")]
75#[derive(Clone, Debug, PartialEq, Eq)]
76pub enum SectionsMode {
77 Auto {
81 include: Vec<Section>,
82 exclude: Vec<Section>,
83 },
84 Explicit(Vec<Section>),
86}
87
88#[cfg(feature = "hotpath-meta")]
89impl Default for SectionsMode {
90 fn default() -> Self {
91 SectionsMode::Auto {
92 include: Vec::new(),
93 exclude: Vec::new(),
94 }
95 }
96}
97
98#[cfg(feature = "hotpath-meta")]
99impl SectionsMode {
100 pub fn parse(spec: &str) -> SectionsMode {
105 let mut auto = false;
106 let mut all = false;
107 let mut include = Vec::new();
108 let mut exclude = Vec::new();
109
110 for part in spec.split(',') {
111 let part = part.trim();
112 match part {
113 "all" => all = true,
114 "auto" => auto = true,
115 _ => {
116 let (target, name) = match part.strip_prefix('-') {
117 Some(rest) => (&mut exclude, rest.trim()),
118 None => (&mut include, part),
119 };
120 if let Some(s) = Section::from_name(name) {
121 if !target.contains(&s) {
122 target.push(s);
123 }
124 } else {
125 eprintln!("[hotpath-meta] Unknown report section: '{}'", part);
126 }
127 }
128 }
129 }
130
131 if all {
132 SectionsMode::Explicit(
133 Section::all()
134 .into_iter()
135 .filter(|s| !exclude.contains(s))
136 .collect(),
137 )
138 } else if auto || !exclude.is_empty() {
139 SectionsMode::Auto { include, exclude }
140 } else {
141 SectionsMode::Explicit(include)
142 }
143 }
144
145 pub fn from_env() -> Option<SectionsMode> {
146 std::env::var("HOTPATH_META_REPORT")
147 .ok()
148 .map(|val| SectionsMode::parse(&val))
149 }
150
151 pub fn explicitly_contains(&self, section: Section) -> bool {
155 match self {
156 SectionsMode::Explicit(list) => list.contains(§ion),
157 SectionsMode::Auto { include, .. } => include.contains(§ion),
158 }
159 }
160
161 #[cfg(feature = "hotpath-cpu-meta")]
164 pub fn contains_or_auto(&self, section: Section) -> bool {
165 match self {
166 SectionsMode::Explicit(list) => list.contains(§ion),
167 SectionsMode::Auto { include, exclude } => {
168 include.contains(§ion) || !exclude.contains(§ion)
169 }
170 }
171 }
172}
173
174#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
193pub enum Format {
194 #[default]
195 Table,
196 Json,
197 JsonPretty,
198 None,
199}
200
201impl FromStr for Format {
202 type Err = String;
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 match s.to_lowercase().as_str() {
206 "table" => Ok(Format::Table),
207 "json" => Ok(Format::Json),
208 "json-pretty" | "jsonpretty" => Ok(Format::JsonPretty),
209 "none" => Ok(Format::None),
210 _ => Err(format!(
211 "unknown format '{}', expected: table, json, json-pretty, none",
212 s
213 )),
214 }
215 }
216}
217
218impl Format {
219 pub fn from_env() -> Self {
222 match std::env::var("HOTPATH_META_OUTPUT_FORMAT") {
223 Ok(v) => v
224 .parse()
225 .unwrap_or_else(|e| panic!("HOTPATH_META_OUTPUT_FORMAT: {}", e)),
226 Err(_) => Format::default(),
227 }
228 }
229}
230
231pub trait IntoF64 {
232 fn into_f64(self) -> f64;
233}
234
235impl IntoF64 for f64 {
236 fn into_f64(self) -> f64 {
237 self
238 }
239}
240
241impl IntoF64 for f32 {
242 fn into_f64(self) -> f64 {
243 self as f64
244 }
245}
246
247impl IntoF64 for i8 {
248 fn into_f64(self) -> f64 {
249 self as f64
250 }
251}
252
253impl IntoF64 for i16 {
254 fn into_f64(self) -> f64 {
255 self as f64
256 }
257}
258
259impl IntoF64 for i32 {
260 fn into_f64(self) -> f64 {
261 self as f64
262 }
263}
264
265impl IntoF64 for i64 {
266 fn into_f64(self) -> f64 {
267 self as f64
268 }
269}
270
271impl IntoF64 for u8 {
272 fn into_f64(self) -> f64 {
273 self as f64
274 }
275}
276
277impl IntoF64 for u16 {
278 fn into_f64(self) -> f64 {
279 self as f64
280 }
281}
282
283impl IntoF64 for u32 {
284 fn into_f64(self) -> f64 {
285 self as f64
286 }
287}
288
289impl IntoF64 for u64 {
290 fn into_f64(self) -> f64 {
291 self as f64
292 }
293}
294
295impl IntoF64 for isize {
296 fn into_f64(self) -> f64 {
297 self as f64
298 }
299}
300
301impl IntoF64 for usize {
302 fn into_f64(self) -> f64 {
303 self as f64
304 }
305}
306
307#[doc(hidden)]
308pub fn env_flag(name: &str) -> bool {
309 std::env::var(name)
310 .map(|v| v.eq_ignore_ascii_case("true") || v == "1")
311 .unwrap_or(false)
312}
313
314#[cfg(feature = "hotpath-meta")]
315pub(crate) fn resolve_timeout_duration(
316 default_duration: std::time::Duration,
317 env_var: &str,
318) -> Option<std::time::Duration> {
319 let effective_duration = std::env::var(env_var)
320 .ok()
321 .and_then(|value| value.parse::<u64>().ok())
322 .map(std::time::Duration::from_millis)
323 .unwrap_or(default_duration);
324
325 if effective_duration.is_zero() {
326 None
327 } else {
328 Some(effective_duration)
329 }
330}