1use rustc_hash::FxHashMap;
2
3use crate::types::{
4 BoxFuture, CustomChartConfig, CustomChartData, CustomTableConfig, CustomTableData,
5 FocusedPaneInfo, GaugePaneConfig, GaugePaneData, HttpError, HttpResponse, LogLevel,
6 NotificationLevel, PluginHost, StatPaneConfig, StatPaneData, Theme,
7};
8
9pub struct HeadlessPluginHost;
14
15impl PluginHost for HeadlessPluginHost {
16 fn notify(&self, level: NotificationLevel, message: &str) {
17 let prefix = match level {
18 NotificationLevel::Info => "info",
19 NotificationLevel::Warning => "warn",
20 NotificationLevel::Error => "error",
21 };
22 println!("[{prefix}] {message}");
23 }
24
25 fn request_repaint(&self) {
26 }
28
29 fn log(&self, level: LogLevel, message: &str) {
30 match level {
31 LogLevel::Debug => log::debug!("{message}"),
32 LogLevel::Info => log::info!("{message}"),
33 LogLevel::Warn => log::warn!("{message}"),
34 LogLevel::Error => log::error!("{message}"),
35 }
36 }
37
38 fn version(&self) -> &'static str {
39 env!("CARGO_PKG_VERSION")
40 }
41
42 fn is_wasm(&self) -> bool {
43 false
44 }
45
46 fn theme(&self) -> Theme {
47 Theme::Dark
48 }
49
50 fn theme_name(&self) -> &'static str {
51 "dark"
52 }
53
54 fn clipboard_write(&self, _text: &str) -> bool {
55 false
56 }
57
58 fn clipboard_read(&self) -> Option<String> {
59 None
60 }
61
62 fn spawn(&self, _future: BoxFuture<()>) {
63 log::warn!("spawn() not available in headless mode");
64 }
65
66 fn http_get(
67 &self,
68 url: &str,
69 headers: &FxHashMap<String, String>,
70 ) -> Result<HttpResponse, HttpError> {
71 let mut request = ureq::get(url);
72 for (key, value) in headers {
73 request = request.header(key.as_str(), value.as_str());
74 }
75
76 match request.call() {
77 Ok(response) => {
78 let status = response.status().as_u16();
79 let mut response_headers = FxHashMap::default();
80 for (name, value) in response.headers().iter() {
81 if let Ok(v) = value.to_str() {
82 response_headers.insert(name.to_string(), v.to_string());
83 }
84 }
85 match response.into_body().read_to_string() {
86 Ok(body) => Ok(HttpResponse {
87 status,
88 body,
89 headers: response_headers,
90 }),
91 Err(e) => Err(HttpError {
92 message: format!("Failed to read response body: {e}"),
93 }),
94 }
95 }
96 Err(e) => Err(HttpError {
97 message: format!("HTTP GET failed: {e}"),
98 }),
99 }
100 }
101
102 fn http_post(
103 &self,
104 url: &str,
105 body: &str,
106 headers: &FxHashMap<String, String>,
107 ) -> Result<HttpResponse, HttpError> {
108 let mut request = ureq::post(url);
109 for (key, value) in headers {
110 request = request.header(key.as_str(), value.as_str());
111 }
112 if !headers.contains_key("Content-Type") && !headers.contains_key("content-type") {
113 request = request.header("Content-Type", "application/json");
114 }
115
116 match request.send(body) {
117 Ok(response) => {
118 let status = response.status().as_u16();
119 let mut response_headers = FxHashMap::default();
120 for (name, value) in response.headers().iter() {
121 if let Ok(v) = value.to_str() {
122 response_headers.insert(name.to_string(), v.to_string());
123 }
124 }
125 match response.into_body().read_to_string() {
126 Ok(resp_body) => Ok(HttpResponse {
127 status,
128 body: resp_body,
129 headers: response_headers,
130 }),
131 Err(e) => Err(HttpError {
132 message: format!("Failed to read response body: {e}"),
133 }),
134 }
135 }
136 Err(e) => Err(HttpError {
137 message: format!("HTTP POST failed: {e}"),
138 }),
139 }
140 }
141
142 fn add_query_pane(&self, _query: &str, _title: Option<&str>) {}
145 fn add_logs_pane(&self) {}
146 fn add_tracing_pane(&self, _trace_id: Option<&str>) {}
147 fn add_terminal_pane(&self) {}
148 fn add_sql_pane(&self) {}
149 fn close_focused_pane(&self) {}
150 fn focus_pane(&self, _direction: &str) {}
151
152 fn set_time_range_preset(&self, _preset: &str) {}
155 fn set_time_range_absolute(&self, _start_secs: f64, _end_secs: f64) {}
156 fn get_time_range(&self) -> (f64, f64) {
157 (0.0, 0.0)
158 }
159
160 fn register_custom_table_pane(&self, _config: CustomTableConfig) {}
163 fn add_custom_table_pane(&self, _pane_type: &str) {}
164 fn update_custom_table_data(&self, _pane_id: usize, _data: CustomTableData) {}
165 fn update_custom_table_data_by_type(&self, _pane_type: &str, _data: CustomTableData) {}
166
167 fn register_custom_chart_pane(&self, _config: CustomChartConfig) {}
168 fn add_custom_chart_pane(&self, _pane_type: &str) {}
169 fn update_custom_chart_data_by_type(&self, _pane_type: &str, _data: CustomChartData) {}
170
171 fn register_stat_pane(&self, _config: StatPaneConfig) {}
172 fn add_stat_pane(&self, _pane_type: &str) {}
173 fn update_stat_data_by_type(&self, _pane_type: &str, _data: StatPaneData) {}
174
175 fn register_gauge_pane(&self, _config: GaugePaneConfig) {}
176 fn add_gauge_pane(&self, _pane_type: &str) {}
177 fn update_gauge_data_by_type(&self, _pane_type: &str, _data: GaugePaneData) {}
178
179 fn get_focused_pane_info(&self) -> Option<FocusedPaneInfo> {
180 None
181 }
182}