Skip to main content

hotpath_meta/
shared.rs

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    Threads,
12    Debug,
13}
14
15impl Section {
16    pub fn all() -> Vec<Section> {
17        vec![
18            Section::FunctionsTiming,
19            Section::FunctionsAlloc,
20            Section::FunctionsCpu,
21            Section::Channels,
22            Section::Streams,
23            Section::Futures,
24            Section::Threads,
25            Section::Debug,
26        ]
27    }
28
29    pub fn short_name(&self) -> &'static str {
30        match self {
31            Section::FunctionsTiming => "timing",
32            Section::FunctionsAlloc => "alloc",
33            Section::FunctionsCpu => "cpu",
34            Section::Channels => "channels",
35            Section::Streams => "streams",
36            Section::Futures => "futures",
37            Section::Threads => "threads",
38            Section::Debug => "debug",
39        }
40    }
41
42    pub fn from_name(s: &str) -> Option<Section> {
43        match s.trim() {
44            "functions-timing" => Some(Section::FunctionsTiming),
45            "functions-alloc" => Some(Section::FunctionsAlloc),
46            "functions-cpu" => Some(Section::FunctionsCpu),
47            "channels" => Some(Section::Channels),
48            "streams" => Some(Section::Streams),
49            "futures" => Some(Section::Futures),
50            "threads" => Some(Section::Threads),
51            "debug" => Some(Section::Debug),
52            _ => None,
53        }
54    }
55
56    pub fn from_env() -> Option<Vec<Section>> {
57        std::env::var("HOTPATH_META_REPORT").ok().map(|val| {
58            let mut sections = Vec::new();
59            for part in val.split(',') {
60                match part.trim() {
61                    "all" => return Section::all(),
62                    other => {
63                        if let Some(s) = Section::from_name(other) {
64                            if !sections.contains(&s) {
65                                sections.push(s);
66                            }
67                        } else {
68                            eprintln!("[hotpath-meta] Unknown report section: '{}'", other);
69                        }
70                    }
71                }
72            }
73            sections
74        })
75    }
76}
77
78/// Output format for profiling reports.
79///
80/// This enum specifies how profiling results should be displayed when the program exits.
81///
82/// # Variants
83///
84/// * `Table` - Human-readable table format (default)
85/// * `Json` - JSON format
86/// * `JsonPretty` - Pretty-printed JSON format
87/// * `None` - Suppress all profiling output (metrics server and MCP server still function)
88///
89/// # Parsing
90///
91/// Can be parsed from strings via `HOTPATH_META_OUTPUT_FORMAT` environment variable:
92/// - `"table"` → `Format::Table`
93/// - `"json"` → `Format::Json`
94/// - `"json-pretty"` → `Format::JsonPretty`
95/// - `"none"` → `Format::None`
96#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
97pub enum Format {
98    #[default]
99    Table,
100    Json,
101    JsonPretty,
102    None,
103}
104
105impl FromStr for Format {
106    type Err = String;
107
108    fn from_str(s: &str) -> Result<Self, Self::Err> {
109        match s.to_lowercase().as_str() {
110            "table" => Ok(Format::Table),
111            "json" => Ok(Format::Json),
112            "json-pretty" | "jsonpretty" => Ok(Format::JsonPretty),
113            "none" => Ok(Format::None),
114            _ => Err(format!(
115                "unknown format '{}', expected: table, json, json-pretty, none",
116                s
117            )),
118        }
119    }
120}
121
122impl Format {
123    /// Returns the format from `HOTPATH_META_OUTPUT_FORMAT` env var, or default if not set.
124    /// Panics if the env var contains an invalid value.
125    pub fn from_env() -> Self {
126        match std::env::var("HOTPATH_META_OUTPUT_FORMAT") {
127            Ok(v) => v
128                .parse()
129                .unwrap_or_else(|e| panic!("HOTPATH_META_OUTPUT_FORMAT: {}", e)),
130            Err(_) => Format::default(),
131        }
132    }
133}
134
135pub trait IntoF64 {
136    fn into_f64(self) -> f64;
137}
138
139impl IntoF64 for f64 {
140    fn into_f64(self) -> f64 {
141        self
142    }
143}
144
145impl IntoF64 for f32 {
146    fn into_f64(self) -> f64 {
147        self as f64
148    }
149}
150
151impl IntoF64 for i8 {
152    fn into_f64(self) -> f64 {
153        self as f64
154    }
155}
156
157impl IntoF64 for i16 {
158    fn into_f64(self) -> f64 {
159        self as f64
160    }
161}
162
163impl IntoF64 for i32 {
164    fn into_f64(self) -> f64 {
165        self as f64
166    }
167}
168
169impl IntoF64 for i64 {
170    fn into_f64(self) -> f64 {
171        self as f64
172    }
173}
174
175impl IntoF64 for u8 {
176    fn into_f64(self) -> f64 {
177        self as f64
178    }
179}
180
181impl IntoF64 for u16 {
182    fn into_f64(self) -> f64 {
183        self as f64
184    }
185}
186
187impl IntoF64 for u32 {
188    fn into_f64(self) -> f64 {
189        self as f64
190    }
191}
192
193impl IntoF64 for u64 {
194    fn into_f64(self) -> f64 {
195        self as f64
196    }
197}
198
199impl IntoF64 for isize {
200    fn into_f64(self) -> f64 {
201        self as f64
202    }
203}
204
205impl IntoF64 for usize {
206    fn into_f64(self) -> f64 {
207        self as f64
208    }
209}
210
211#[doc(hidden)]
212pub fn env_flag(name: &str) -> bool {
213    std::env::var(name)
214        .map(|v| v.eq_ignore_ascii_case("true") || v == "1")
215        .unwrap_or(false)
216}
217
218#[cfg(feature = "hotpath-meta")]
219pub(crate) fn resolve_timeout_duration(
220    default_duration: std::time::Duration,
221    env_var: &str,
222) -> Option<std::time::Duration> {
223    let effective_duration = std::env::var(env_var)
224        .ok()
225        .and_then(|value| value.parse::<u64>().ok())
226        .map(std::time::Duration::from_millis)
227        .unwrap_or(default_duration);
228
229    if effective_duration.is_zero() {
230        None
231    } else {
232        Some(effective_duration)
233    }
234}