1use std::fmt;
4
5use futu_core::log_redact::endpoint_log_fingerprint;
6
7fn listen_port(addr: &str) -> Option<&str> {
8 let trimmed = addr.trim();
9 if let Some((_, port)) = trimmed.rsplit_once("]:") {
10 return (!port.is_empty()).then_some(port);
11 }
12 trimmed
13 .rsplit_once(':')
14 .and_then(|(_, port)| (!port.is_empty()).then_some(port))
15}
16
17#[must_use]
19pub fn bind_error_message(
20 surface: &str,
21 flag_name: &str,
22 listen_addr: &str,
23 error: impl fmt::Display,
24) -> String {
25 let inspect = listen_port(listen_addr)
26 .map(|port| format!("lsof -nP -iTCP:{port} -sTCP:LISTEN"))
27 .unwrap_or_else(|| "lsof -nP -iTCP -sTCP:LISTEN".to_string());
28
29 format!(
30 "{surface} listener failed to bind {listen_addr}: {error}. \
31 Hint: another futu-opend or local process may already own this port. \
32 Change {flag_name}, stop the old process, or inspect with `{inspect}`."
33 )
34}
35
36fn io_bind_error_message(
37 surface: &str,
38 flag_name: &str,
39 listen_addr: &str,
40 error: &std::io::Error,
41) -> String {
42 let endpoint_fingerprint = endpoint_log_fingerprint(listen_addr);
43
44 format!(
45 "{surface} listener failed to bind endpoint_fingerprint={endpoint_fingerprint} \
46 error_kind={:?}: {error}. \
47 Hint: another futu-opend or local process may already own this port. \
48 Change {flag_name}, stop the old process, or inspect with \
49 `lsof -nP -iTCP -sTCP:LISTEN`.",
50 error.kind(),
51 )
52}
53
54pub fn invalid_addr_error(
56 surface: &str,
57 flag_name: &str,
58 listen_addr: &str,
59 error: impl fmt::Display,
60) -> std::io::Error {
61 let endpoint_fingerprint = endpoint_log_fingerprint(listen_addr);
62 std::io::Error::new(
63 std::io::ErrorKind::InvalidInput,
64 format!(
65 "{surface} listener address is invalid \
66 endpoint_fingerprint={endpoint_fingerprint} error_kind=InvalidInput: {error}. \
67 Hint: correct {flag_name} and restart."
68 ),
69 )
70}
71
72pub fn listener_runtime_error(
74 surface: &str,
75 listen_addr: &str,
76 error: impl fmt::Display,
77) -> std::io::Error {
78 let endpoint_fingerprint = endpoint_log_fingerprint(listen_addr);
79 std::io::Error::other(format!(
80 "{surface} listener runtime failed endpoint_fingerprint={endpoint_fingerprint}: {error}"
81 ))
82}
83
84#[must_use]
87pub fn port_conflict_message(surface: &str, flag_name: &str, listen_addr: &str) -> String {
88 let inspect = listen_port(listen_addr)
89 .map(|port| format!("lsof -nP -iTCP:{port} -sTCP:LISTEN"))
90 .unwrap_or_else(|| "lsof -nP -iTCP -sTCP:LISTEN".to_string());
91
92 format!(
93 "{surface} port {listen_addr} already accepts TCP connections. \
94 Hint: another futu-opend or local process may be running. \
95 Change {flag_name}, stop the old process, or inspect with `{inspect}`."
96 )
97}
98
99pub fn io_bind_error(
102 surface: &str,
103 flag_name: &str,
104 listen_addr: &str,
105 error: std::io::Error,
106) -> std::io::Error {
107 let kind = error.kind();
108 std::io::Error::new(
109 kind,
110 io_bind_error_message(surface, flag_name, listen_addr, &error),
111 )
112}
113
114#[cfg(test)]
115mod tests;