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