//! Helper functions and macros. use std::{ io::{Result, Write}, process::{self, Stdio}, }; /// Debug macro that appends a line to `phetch.log`. /// Useful for printf-style debugging - add your `log!()` calls, /// and `tail -f phetch.log` while running phetch to peek inside. #[allow(unused_macros)] macro_rules! log { ($e:expr) => {{ if cfg!(debug_assertions) { if let Ok(mut file) = std::fs::OpenOptions::new() .append(true) .create(true) .open("phetch.log") { use std::io::prelude::*; file.write($e.as_ref()); file.write(b"\n"); } } }}; ($e:expr, $($y:expr),*) => { if cfg!(debug_assertions) { log!(format!($e, $($y),*)); } }; } /// Creates an Other kind of io::Error. macro_rules! error { ($e:expr) => { std::io::Error::new(std::io::ErrorKind::Other, $e) }; ($e:expr, $($y:expr),*) => { error!(format!($e, $($y),*)); }; } /// Number of bytes in a human-ish readable format. pub fn human_bytes(bytes: usize) -> String { let (count, tag) = if bytes < 1000 { (bytes, " bytes") } else if bytes < 1_000_000 { (bytes / 1000, "Kb") } else if bytes < 1_000_000_000 { (bytes / 1_000_000, "Mb") } else { (bytes / 1_000_000_000, "Gb") }; format!("{}{}", count, tag) } /// Copies data to the system clipboard, if possible. /// Uses `pbcopy` on macOS or `xclip -sel clip` on Linux. pub fn copy_to_clipboard(data: &str) -> Result<()> { #[cfg(target_os = "macos")] let mut cmd = process::Command::new("pbcopy"); #[cfg(not(target_os = "macos"))] let mut cmd = process::Command::new("xclip"); #[cfg(not(target_os = "macos"))] let cmd = cmd.args(&["-sel", "clip"]); cmd.stdin(Stdio::piped()) .spawn() .and_then(|mut child| { let child_stdin = child.stdin.as_mut().unwrap(); child_stdin.write_all(data.as_bytes()) }) .map_err(|e| error!("Clipboard error: {}", e)) } /// Used to open non-Gopher URLs. /// Runs `open` command on macOS or `xdg-open` on Linux. pub fn open_external(url: &str) -> Result<()> { #[cfg(target_os = "macos")] let cmd = "open"; #[cfg(not(target_os = "macos"))] let cmd = "xdg-open"; let output = process::Command::new(cmd) .arg(url) .output() .map_err(|e| error!("`open` error: {}", e))?; if output.stderr.is_empty() { Ok(()) } else { Err(error!( "`open` error: {}", String::from_utf8_lossy(&output.stderr).trim_end() )) } }