use crate::model::RunResult;
use anyhow::{Context, Result};
use std::sync::mpsc as std_mpsc;
use std::sync::OnceLock;
use std::time::Duration;
use super::state::UiState;
static CLIPBOARD_SENDER: OnceLock<std_mpsc::Sender<String>> = OnceLock::new();
pub fn enrich_result_with_network_info(r: &RunResult, state: &UiState) -> RunResult {
let network_info = crate::network::NetworkInfo {
interface_name: state.interface_name.clone(),
network_name: state.network_name.clone(),
is_wireless: state.is_wireless,
interface_mac: state.interface_mac.clone(),
local_ipv4: state.local_ipv4.clone(),
local_ipv6: state.local_ipv6.clone(),
};
let mut enriched = crate::network::enrich_result(r, &network_info);
enriched.ip = state.ip.clone();
enriched.colo = state.colo.clone();
enriched.asn = state.asn.clone();
enriched.as_org = state.as_org.clone();
if enriched.server.is_none() {
enriched.server = state.server.clone();
}
enriched
}
pub fn save_result_json(r: &RunResult, state: &UiState) -> Result<std::path::PathBuf> {
let enriched = enrich_result_with_network_info(r, state);
crate::storage::save_run(&enriched)
}
pub fn save_and_show_path(r: &RunResult, state: &mut UiState) {
match save_result_json(r, state) {
Ok(path) => {
let enriched = enrich_result_with_network_info(r, state);
state.last_result = Some(enriched);
if path.exists() {
let line = crate::event_format::format_saved_line(&path);
state.info = line.clone();
UiState::push_log_line(&mut state.text_log, line);
} else {
state.info = format!("Saved (verifying): {}", path.display());
}
}
Err(e) => {
state.info = format!("Save failed: {e:#}");
}
}
}
pub fn export_result_json(r: &RunResult, state: &UiState) -> Result<std::path::PathBuf> {
let default_name = format!(
"cloudflare-speed-{}-{}.json",
r.timestamp_utc.replace(':', "-").replace('T', "_"),
&r.meas_id[..8.min(r.meas_id.len())]
);
let current_dir = std::env::current_dir().context("get current directory")?;
let path = current_dir.join(default_name);
let enriched = enrich_result_with_network_info(r, state);
crate::storage::export_json(&path, &enriched)?;
Ok(path)
}
pub fn export_result_csv(r: &RunResult, state: &UiState) -> Result<std::path::PathBuf> {
let default_name = format!(
"cloudflare-speed-{}-{}.csv",
r.timestamp_utc.replace(':', "-").replace('T', "_"),
&r.meas_id[..8.min(r.meas_id.len())]
);
let current_dir = std::env::current_dir().context("get current directory")?;
let path = current_dir.join(default_name);
let enriched = enrich_result_with_network_info(r, state);
crate::storage::export_csv(&path, &enriched)?;
Ok(path)
}
fn init_clipboard_manager() -> Result<&'static std_mpsc::Sender<String>> {
CLIPBOARD_SENDER.get_or_init(|| {
let (tx, rx) = std_mpsc::channel::<String>();
std::thread::spawn(move || {
use arboard::Clipboard;
for text in rx {
if let Ok(mut clipboard) = Clipboard::new() {
if clipboard.set_text(&text).is_ok() {
std::thread::sleep(Duration::from_secs(2));
}
}
}
});
tx
});
CLIPBOARD_SENDER
.get()
.ok_or_else(|| anyhow::anyhow!("Failed to initialize clipboard manager"))
}
pub fn copy_to_clipboard(text: &str) -> Result<()> {
let sender = init_clipboard_manager()?;
sender
.send(text.to_string())
.map_err(|_| anyhow::anyhow!("Clipboard manager channel closed"))?;
Ok(())
}