use std::net::SocketAddr;
use super::env;
pub const CA_SERVER_PORT: u16 = 5064;
pub const CA_REPEATER_PORT: u16 = 5065;
pub const PVA_SERVER_PORT: u16 = 5075;
pub const PVA_BROADCAST_PORT: u16 = 5076;
pub fn ca_server_port() -> u16 {
env::get_u16("EPICS_CA_SERVER_PORT", CA_SERVER_PORT)
}
pub fn cas_server_port() -> u16 {
env::get_u16("EPICS_CAS_SERVER_PORT", ca_server_port())
}
pub fn ca_repeater_port() -> u16 {
env::get_u16("EPICS_CA_REPEATER_PORT", CA_REPEATER_PORT)
}
pub fn ca_mcast_ttl() -> u32 {
const DEFAULT: u32 = 1;
match env::get("EPICS_CA_MCAST_TTL") {
Some(s) => s
.trim()
.parse::<u32>()
.ok()
.filter(|v| *v >= 1 && *v <= 255)
.unwrap_or(DEFAULT),
None => DEFAULT,
}
}
pub fn pva_broadcast_port() -> u16 {
env::get_u16("EPICS_PVA_BROADCAST_PORT", PVA_BROADCAST_PORT)
}
pub fn pva_server_port() -> u16 {
env::get_u16("EPICS_PVA_SERVER_PORT", PVA_SERVER_PORT)
}
pub fn parse_socket_addr(s: &str) -> Result<SocketAddr, std::net::AddrParseError> {
s.parse()
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial(epics_env)]
fn test_default_ca_server_port() {
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
assert_eq!(ca_server_port(), 5064);
}
#[test]
#[serial(epics_env)]
fn test_default_ca_repeater_port() {
unsafe { std::env::remove_var("EPICS_CA_REPEATER_PORT") };
assert_eq!(ca_repeater_port(), 5065);
}
#[test]
#[serial(epics_env)]
fn test_default_pva_broadcast_port() {
unsafe { std::env::remove_var("EPICS_PVA_BROADCAST_PORT") };
assert_eq!(pva_broadcast_port(), 5076);
}
#[test]
#[serial(epics_env)]
fn test_default_pva_server_port() {
unsafe { std::env::remove_var("EPICS_PVA_SERVER_PORT") };
assert_eq!(pva_server_port(), 5075);
}
#[test]
#[serial(epics_env)]
fn test_ca_server_port_env_override() {
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "9064") };
assert_eq!(ca_server_port(), 9064);
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
}
#[test]
#[serial(epics_env)]
fn test_cas_server_port_defaults_to_ca_server_port() {
unsafe {
std::env::remove_var("EPICS_CAS_SERVER_PORT");
std::env::remove_var("EPICS_CA_SERVER_PORT");
}
assert_eq!(cas_server_port(), CA_SERVER_PORT);
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "9064") };
assert_eq!(
cas_server_port(),
9064,
"cas_server_port falls back to EPICS_CA_SERVER_PORT when CAS-specific unset"
);
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
}
#[test]
#[serial(epics_env)]
fn test_ca_mcast_ttl_default() {
unsafe { std::env::remove_var("EPICS_CA_MCAST_TTL") };
assert_eq!(ca_mcast_ttl(), 1);
}
#[test]
#[serial(epics_env)]
fn test_ca_mcast_ttl_override() {
unsafe { std::env::set_var("EPICS_CA_MCAST_TTL", "32") };
assert_eq!(ca_mcast_ttl(), 32);
unsafe { std::env::remove_var("EPICS_CA_MCAST_TTL") };
}
#[test]
#[serial(epics_env)]
fn test_ca_mcast_ttl_clamps_invalid_to_default() {
for bad in ["0", "256", "abc", ""] {
unsafe { std::env::set_var("EPICS_CA_MCAST_TTL", bad) };
assert_eq!(
ca_mcast_ttl(),
1,
"invalid EPICS_CA_MCAST_TTL={bad:?} must fall back to default 1"
);
}
unsafe { std::env::remove_var("EPICS_CA_MCAST_TTL") };
}
#[test]
#[serial(epics_env)]
fn test_cas_server_port_overrides_ca_server_port() {
unsafe {
std::env::set_var("EPICS_CA_SERVER_PORT", "5064");
std::env::set_var("EPICS_CAS_SERVER_PORT", "9064");
}
assert_eq!(
ca_server_port(),
5064,
"client semantic ignores EPICS_CAS_SERVER_PORT"
);
assert_eq!(
cas_server_port(),
9064,
"server-side bind picks up EPICS_CAS_SERVER_PORT"
);
unsafe {
std::env::remove_var("EPICS_CAS_SERVER_PORT");
std::env::remove_var("EPICS_CA_SERVER_PORT");
}
}
#[test]
#[serial(epics_env)]
fn test_ca_server_port_rejects_privileged_port() {
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "80") };
assert_eq!(
ca_server_port(),
CA_SERVER_PORT,
"privileged port 80 must fall back to the default 5064"
);
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "5000") };
assert_eq!(
ca_server_port(),
CA_SERVER_PORT,
"port 5000 (== IPPORT_USERRESERVED) must fall back to default"
);
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "0") };
assert_eq!(ca_server_port(), CA_SERVER_PORT, "port 0 must fall back");
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
}
#[test]
#[serial(epics_env)]
fn test_ca_server_port_accepts_valid_high_port() {
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "5001") };
assert_eq!(ca_server_port(), 5001);
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "6064") };
assert_eq!(ca_server_port(), 6064);
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
}
#[test]
#[serial(epics_env)]
fn test_ca_repeater_port_rejects_out_of_range() {
unsafe { std::env::set_var("EPICS_CA_REPEATER_PORT", "443") };
assert_eq!(ca_repeater_port(), CA_REPEATER_PORT);
unsafe { std::env::remove_var("EPICS_CA_REPEATER_PORT") };
}
#[test]
#[serial(epics_env)]
fn test_port_reader_lenient_parse() {
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", " 6064") };
assert_eq!(ca_server_port(), 6064);
unsafe { std::env::set_var("EPICS_CA_SERVER_PORT", "6064xyz") };
assert_eq!(ca_server_port(), 6064);
unsafe { std::env::remove_var("EPICS_CA_SERVER_PORT") };
}
#[test]
fn test_parse_socket_addr_valid() {
let addr = parse_socket_addr("127.0.0.1:5064").unwrap();
assert_eq!(addr.port(), 5064);
assert_eq!(addr.ip(), std::net::Ipv4Addr::LOCALHOST);
}
#[test]
fn test_parse_socket_addr_invalid() {
assert!(parse_socket_addr("not-an-address").is_err());
}
}