spvirit_tools/spvirit_client/
cli.rs1use std::net::SocketAddr;
2use std::time::Duration;
3
4use argparse::{ArgumentParser, Store, StoreTrue};
5
6use super::search::{is_auto_addr_list_enabled, parse_name_servers};
7use super::types::PvGetOptions;
8
9pub struct CommonClientArgs {
26 pub timeout_secs: u64,
27 pub server: String,
28 pub search_addr: String,
29 pub bind_addr: String,
30 pub name_server: String,
31 pub udp_port: u16,
32 pub tcp_port: u16,
33 pub debug: bool,
34 pub no_broadcast: bool,
35 pub authnz_user: String,
36 pub authnz_host: String,
37}
38
39impl CommonClientArgs {
40 pub fn new() -> Self {
41 Self {
42 timeout_secs: 5,
43 server: String::new(),
44 search_addr: String::new(),
45 bind_addr: String::new(),
46 name_server: String::new(),
47 udp_port: 5076,
48 tcp_port: 5075,
49 debug: false,
50 no_broadcast: false,
51 authnz_user: String::new(),
52 authnz_host: String::new(),
53 }
54 }
55
56 pub fn add_to_parser<'a, 'b>(&'a mut self, ap: &'b mut ArgumentParser<'a>) {
61 ap.refer(&mut self.timeout_secs)
62 .add_option(&["-w", "--timeout"], Store, "Timeout in seconds");
63 ap.refer(&mut self.server)
64 .add_option(&["--server"], Store, "Server address (ip:port)");
65 ap.refer(&mut self.search_addr).add_option(
66 &["--search-addr"],
67 Store,
68 "Search target IP (default EPICS_PVA_ADDR_LIST/auto broadcast)",
69 );
70 ap.refer(&mut self.bind_addr).add_option(
71 &["--bind-addr"],
72 Store,
73 "Local bind IP for UDP search",
74 );
75 ap.refer(&mut self.name_server).add_option(
76 &["--name-server"],
77 Store,
78 "PVA name server address (host:port, repeatable via EPICS_PVA_NAME_SERVERS)",
79 );
80 ap.refer(&mut self.udp_port)
81 .add_option(&["--udp-port"], Store, "UDP search port");
82 ap.refer(&mut self.tcp_port)
83 .add_option(&["--tcp-port"], Store, "TCP server default port");
84 ap.refer(&mut self.debug)
85 .add_option(&["-d", "--debug"], StoreTrue, "Enable debug logging");
86 ap.refer(&mut self.no_broadcast).add_option(
87 &["--no-broadcast"],
88 StoreTrue,
89 "Disable UDP broadcast/multicast search (also set via EPICS_PVA_AUTO_ADDR_LIST=NO)",
90 );
91 ap.refer(&mut self.authnz_user).add_option(
92 &["--authnz-user"],
93 Store,
94 "AuthNZ user override (takes precedence over env)",
95 );
96 ap.refer(&mut self.authnz_host).add_option(
97 &["--authnz-host"],
98 Store,
99 "AuthNZ host override (takes precedence over env)",
100 );
101 }
102
103 pub fn init_tracing(&self) {
105 let max_level = if self.debug {
106 tracing::Level::DEBUG
107 } else {
108 tracing::Level::INFO
109 };
110 tracing_subscriber::fmt().with_max_level(max_level).init();
111 }
112
113 pub fn into_pv_get_options(
115 self,
116 pv_name: String,
117 ) -> Result<PvGetOptions, Box<dyn std::error::Error>> {
118 let mut opts = PvGetOptions::new(pv_name);
119 opts.timeout = Duration::from_secs(self.timeout_secs);
120 opts.udp_port = self.udp_port;
121 opts.tcp_port = self.tcp_port;
122 opts.debug = self.debug;
123 opts.no_broadcast = self.no_broadcast || !is_auto_addr_list_enabled();
124
125 if !self.server.is_empty() {
126 let addr: SocketAddr = self.server.parse()?;
127 opts.server_addr = Some(addr);
128 }
129 if !self.search_addr.is_empty() {
130 opts.search_addr = Some(self.search_addr.parse()?);
131 }
132 if !self.bind_addr.is_empty() {
133 opts.bind_addr = Some(self.bind_addr.parse()?);
134 }
135
136 let mut ns = parse_name_servers(&self.name_server);
137 if let Ok(env) = std::env::var("EPICS_PVA_NAME_SERVERS") {
138 ns.extend(parse_name_servers(&env));
139 }
140 opts.name_servers = ns;
141
142 if !self.authnz_user.is_empty() {
143 opts.authnz_user = Some(self.authnz_user);
144 }
145 if !self.authnz_host.is_empty() {
146 opts.authnz_host = Some(self.authnz_host);
147 }
148
149 Ok(opts)
150 }
151}