use std::net::IpAddr;
use std::str::FromStr;
use std::sync::LazyLock;
use regex::Regex;
use tracing::Level;
use tracing_subscriber::FmtSubscriber;
static DOMAIN_NAME_PATTERN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.?$").unwrap());
pub fn check_host(host: &str) -> Result<(), String> {
if IpAddr::from_str(host).is_err() {
if !DOMAIN_NAME_PATTERN.is_match(host) {
return Err("Host portion is not IPv4, IPv6, or domain name".to_string());
}
}
Ok(())
}
pub fn parse_address(addr: &str) -> Result<(&str, u16), String> {
match addr.rsplit_once(':') {
None => Err("Missing port. Use HOST:PORT format.".to_string()),
Some((host, port)) => {
check_host(host)?;
let port = match port.parse::<u16>() {
Ok(parsed) => parsed,
Err(err) => return Err(format!("Invalid port: {err}")),
};
Ok((host, port))
}
}
}
pub fn shell_escape(raw: &str) -> String {
shlex::try_quote(raw).unwrap().into()
}
pub fn configure_logging() {
if std::env::var("BM_DEBUG").unwrap_or_default() == "true" {
let trace_subscriber = FmtSubscriber::builder()
.with_max_level(Level::DEBUG)
.finish();
tracing::subscriber::set_global_default(trace_subscriber).unwrap();
}
}