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