//! Utilities. use home_dir::HomeDirExt; use lazy_static::lazy_static; use log::debug; use regex::Regex; use std::net::TcpStream; use std::path::{Path, PathBuf}; const SSH_PORT: i32 = 22; // Wait for a virtual machine to have opened its SSH port. pub fn wait_for_ssh(name: &str) { debug!("waiting for {} to respond to SSH", name); let addr = format!("{}:{}", name, SSH_PORT); loop { if TcpStream::connect(&addr).is_ok() { return; } } } /// Expand a ~/ at the beginning of a Path to refer to the home directory. pub fn expand_tilde(path: &Path) -> Result { path.expand_home() } pub fn expand_optional_pathbuf(maybe_path: &mut Option) -> Result<(), home_dir::Error> { if let Some(path) = maybe_path { *maybe_path = Some(expand_tilde(path)?); } Ok(()) } pub fn expand_optional_pathbufs( maybe_paths: &mut Option>, ) -> Result<(), home_dir::Error> { if let Some(paths) = maybe_paths { let mut expanded = vec![]; for path in paths { expanded.push(expand_tilde(path)?); } *maybe_paths = Some(expanded); } Ok(()) } pub fn check_network_names(networks: &[String]) -> Result<(), NetworkNameError> { lazy_static! { static ref RE: Regex = Regex::new("^(network|bridge)=[a-z0-9-]*$").unwrap(); } for name in networks.iter() { if !RE.is_match(name) { return Err(NetworkNameError::BadNetworkName(name.to_string())); } } Ok(()) } #[derive(Debug, thiserror::Error)] pub enum NetworkNameError { /// Bad network name. #[error("Network name should be network=foo or bridge=bar, not: {0}")] BadNetworkName(String), } #[cfg(test)] mod test { use super::check_network_names; fn networks(names: &[&str]) -> Vec { names.iter().map(|s| s.to_string()).collect() } #[test] fn accepts_network_name() { let names = networks(&["network=foo"]); assert!(check_network_names(&names).is_ok()); } #[test] fn accepts_bridge_name() { let names = networks(&["bridge=foo"]); assert!(check_network_names(&names).is_ok()); } #[test] fn rejects_plain_name() { let names = networks(&["foo"]); assert!(check_network_names(&names).is_err()); } }