use std::time::Duration;
const SCAN_START: u16 = 9000;
const SCAN_END: u16 = 9010;
pub fn resolve(uri: &str) -> Result<String, String> {
if uri.starts_with("http://") || uri.starts_with("https://") {
return Err(format!(
"Plain HTTP URL '{}' is not allowed — use zc://node-name instead.",
uri
));
}
let rest = match uri.strip_prefix("zc://") {
Some(r) => r.trim_end_matches('/'),
None => {
return Err(format!(
"Invalid broker URI '{}'. Expected zc://node-name.",
uri
))
}
};
if rest.is_empty() {
return Err("Empty broker name in zc:// URI.".to_string());
}
let (name, explicit_port): (&str, Option<u16>) = if let Some(colon) = rest.rfind(':') {
let port_str = &rest[colon + 1..];
match port_str.parse::<u16>() {
Ok(p) => (&rest[..colon], Some(p)),
Err(_) => (rest, None),
}
} else {
(rest, None)
};
let looks_like_ip =
name.split('.').all(|octet| octet.parse::<u8>().is_ok()) && name.contains('.');
if looks_like_ip {
let port = explicit_port.unwrap_or(9000);
return Ok(format!("http://{}:{}", name, port));
}
let agent = ureq::Agent::new_with_config(
ureq::Agent::config_builder()
.timeout_connect(Some(Duration::from_millis(300)))
.timeout_recv_response(Some(Duration::from_millis(500)))
.build(),
);
let ports: Vec<u16> = if let Some(p) = explicit_port {
vec![p]
} else {
(SCAN_START..=SCAN_END).collect()
};
let wildcard = name == "localhost" || name == "127.0.0.1";
for port in &ports {
let url = format!("http://localhost:{}/health", port);
if let Ok(resp) = agent.get(&url).call() {
if let Ok(text) = resp.into_body().read_to_string() {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&text) {
if wildcard || json["node_name"].as_str() == Some(name) {
return Ok(format!("http://localhost:{}", port));
}
}
}
}
}
if let Some(p) = explicit_port {
Err(format!("Broker '{}' not found on localhost:{}", name, p))
} else {
Err(format!(
"no local broker on localhost:{}-{}.\n \
Start one with `zc broker` (or `zc up` for a broker + worker), or target a\n \
mesh broker with `ZAKURO_BROKER=zc://node-<id> zc workers` (list them: `zc brokers`).",
SCAN_START, SCAN_END
))
}
}