use anyhow::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortFormat {
Port,
Addr,
Json,
}
pub fn parse_port_from_addr(addr: &str) -> Option<u16> {
let colon = addr.rfind(':')?;
addr[colon + 1..].parse::<u16>().ok()
}
pub fn format_output(addr: &str, format: PortFormat) -> Option<String> {
match format {
PortFormat::Port => {
let port = parse_port_from_addr(addr)?;
Some(port.to_string())
}
PortFormat::Addr => Some(addr.to_string()),
PortFormat::Json => {
let port = parse_port_from_addr(addr)?;
let colon = addr.rfind(':')?;
let host_part = &addr[..colon];
let host = host_part.trim_matches(|c| c == '[' || c == ']');
Some(format!(r#"{{"addr":"{host}","port":{port}}}"#))
}
}
}
pub fn handle_port(format: PortFormat) -> Result<()> {
let addr = match trusty_common::read_daemon_addr("trusty-review") {
Ok(Some(a)) if !a.is_empty() => a,
Ok(Some(_)) | Ok(None) => {
anyhow::bail!(
"trusty-review: no daemon running (address file not found). \
Start with `trusty-review serve`."
);
}
Err(e) => {
anyhow::bail!("trusty-review: could not read daemon address: {e:#}");
}
};
match format_output(&addr, format) {
Some(out) => {
println!("{out}");
Ok(())
}
None => {
anyhow::bail!(
"trusty-review: daemon address file contains an unrecognised \
address `{addr}` (expected host:port). \
Re-start the daemon with `trusty-review serve`."
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_output_port_default() {
assert_eq!(
format_output("127.0.0.1:7880", PortFormat::Port),
Some("7880".to_string())
);
}
#[test]
fn format_output_addr() {
assert_eq!(
format_output("127.0.0.1:7880", PortFormat::Addr),
Some("127.0.0.1:7880".to_string())
);
}
#[test]
fn format_output_json() {
assert_eq!(
format_output("127.0.0.1:7880", PortFormat::Json),
Some(r#"{"addr":"127.0.0.1","port":7880}"#.to_string())
);
}
#[test]
fn parse_port_ipv6() {
assert_eq!(parse_port_from_addr("[::1]:7880"), Some(7880));
}
#[test]
fn format_output_json_ipv6_strips_brackets() {
assert_eq!(
format_output("[::1]:7880", PortFormat::Json),
Some(r#"{"addr":"::1","port":7880}"#.to_string())
);
}
#[test]
fn format_output_malformed_returns_none() {
assert_eq!(format_output("not-an-addr", PortFormat::Port), None);
assert_eq!(format_output("", PortFormat::Port), None);
}
#[test]
fn format_output_json_malformed_returns_none() {
assert_eq!(format_output("127.0.0.1:notaport", PortFormat::Json), None);
}
#[test]
fn parse_port_standard() {
assert_eq!(parse_port_from_addr("127.0.0.1:7880"), Some(7880));
}
#[test]
fn parse_port_zero() {
assert_eq!(parse_port_from_addr("127.0.0.1:0"), Some(0));
}
#[test]
fn parse_port_no_colon() {
assert_eq!(parse_port_from_addr("127.0.0.1"), None);
}
#[test]
fn parse_port_overflow() {
assert_eq!(parse_port_from_addr("127.0.0.1:99999"), None);
}
}