1use bon::Builder;
2use proptest_derive::Arbitrary;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use crate::common::OnOff;
7use crate::to_command::{ToArg, ToCommand};
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
12pub struct VC {
13 is_pixel: bool,
14 w: usize,
15 h: usize,
16}
17
18#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
19pub struct Udp {
20 remote_host: Option<String>,
21 remote_port: u16,
22 src_ip: Option<String>,
23 src_port: Option<u16>,
24}
25
26#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
27pub struct Tcp {
28 host: String,
29 port: u16,
30 server: Option<OnOff>,
31 wait: Option<OnOff>,
32 nodelay: Option<OnOff>,
33 reconnect_ms: Option<usize>,
34}
35
36#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
37pub struct Telnet {
38 host: String,
39 port: u16,
40 server: Option<OnOff>,
41 wait: Option<OnOff>,
42 nodelay: Option<OnOff>,
43}
44
45#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
46pub struct Websocket {
47 host: String,
48 port: u16,
49 server: Option<OnOff>,
50 wait: Option<OnOff>,
51 nodelay: Option<OnOff>,
52}
53
54#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
55pub struct Unix {
56 path: PathBuf,
57 server: Option<OnOff>,
58 wait: Option<OnOff>,
59 reconnect_ms: Option<usize>,
60}
61
62#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
63pub enum SpecialDevice {
64 VC(Option<VC>),
65 Pty(Option<PathBuf>),
66 None,
67 Null,
68 Chardev(String),
69 Dev(String),
70 Parport(usize),
71 File(PathBuf),
72 Stdio,
73 Pipe(PathBuf),
74 Com(usize),
75 Udp(Udp),
76 Tcp(Tcp),
77 Telnet(Telnet),
78 Websocket(Websocket),
79 Unix(Unix),
80 Mon(String),
81 Braille,
82 Msmouse,
83}
84
85impl ToCommand for SpecialDevice {
86 fn to_args(&self) -> Vec<String> {
87 let mut args = vec![];
88
89 match self {
90 SpecialDevice::VC(vc) => {
91 if let Some(vc) = vc {
92 if vc.is_pixel {
93 args.push(format!("vc:{}x{}", vc.w, vc.h));
94 } else {
95 args.push(format!("vc:{}Cx{}C", vc.w, vc.h));
96 }
97 } else {
98 args.push("vc".to_string());
99 }
100 }
101 SpecialDevice::Pty(pty) => {
102 if let Some(pty) = pty {
103 args.push(format!("pty:{}", pty.display()));
104 } else {
105 args.push("pty".to_string());
106 }
107 }
108 SpecialDevice::None => {
109 args.push("none".to_string());
110 }
111 SpecialDevice::Null => {
112 args.push("null".to_string());
113 }
114 SpecialDevice::Chardev(chardev) => {
115 args.push(format!("chardev:{}", chardev));
116 }
117 SpecialDevice::Dev(dev) => {
118 args.push(format!("/dev/{}", dev));
119 }
120 SpecialDevice::Parport(parport) => {
121 args.push(format!("/dev/parport{}", parport));
122 }
123 SpecialDevice::File(file) => {
124 args.push(format!("file:{}", file.display()));
125 }
126 SpecialDevice::Stdio => {
127 args.push("stdio".to_string());
128 }
129 SpecialDevice::Pipe(pipe) => {
130 args.push(format!("pipe:{}", pipe.display()));
131 }
132 SpecialDevice::Com(com) => {
133 args.push(format!("COM{}", com));
134 }
135 SpecialDevice::Udp(udp) => {
136 let mut udpargs = vec![];
137
138 udpargs.push("udp".to_string());
139 if let Some(remote_host) = &udp.remote_host {
140 udpargs.push(remote_host.to_string());
141 }
142 udpargs.push(format!("{}", udp.remote_port));
143 if let Some(src_ip) = &udp.src_ip {
144 udpargs.push(format!("@{}", src_ip));
145 }
146 if let Some(src_port) = &udp.src_port {
147 udpargs.push(format!("{}", src_port));
148 }
149 args.push(udpargs.join(":"));
150 }
151 SpecialDevice::Tcp(tcp) => {
152 let mut tcpargs = vec![];
153
154 tcpargs.push(format!("tcp:{}:{}", tcp.host, tcp.port));
155 if let Some(server) = &tcp.server {
156 tcpargs.push(format!("server={}", server.to_arg()));
157 }
158 if let Some(wait) = &tcp.wait {
159 tcpargs.push(format!("wait={}", wait.to_arg()));
160 }
161 if let Some(nodelay) = &tcp.nodelay {
162 tcpargs.push(format!("nodelay={}", nodelay.to_arg()));
163 }
164 if let Some(reconnect_ms) = &tcp.reconnect_ms {
165 tcpargs.push(format!("reconnect-ms={}", reconnect_ms));
166 }
167 args.push(tcpargs.join(","));
168 }
169 SpecialDevice::Telnet(telnet) => {
170 let mut telnetargs = vec![];
171
172 telnetargs.push(format!("telnet:{}:{}", telnet.host, telnet.port));
173 if let Some(server) = &telnet.server {
174 telnetargs.push(format!("server={}", server.to_arg()));
175 }
176 if let Some(wait) = &telnet.wait {
177 telnetargs.push(format!("wait={}", wait.to_arg()));
178 }
179 if let Some(nodelay) = &telnet.nodelay {
180 telnetargs.push(format!("nodelay={}", nodelay.to_arg()));
181 }
182 args.push(telnetargs.join(","));
183 }
184 SpecialDevice::Websocket(ws) => {
185 let mut wsargs = vec![];
186
187 wsargs.push(format!("websocket:{}:{}", ws.host, ws.port));
188 if let Some(server) = &ws.server {
189 wsargs.push(format!("server={}", server.to_arg()));
190 }
191 if let Some(wait) = &ws.wait {
192 wsargs.push(format!("wait={}", wait.to_arg()));
193 }
194 if let Some(nodelay) = &ws.nodelay {
195 wsargs.push(format!("nodelay={}", nodelay.to_arg()));
196 }
197 args.push(wsargs.join(","));
198 }
199 SpecialDevice::Unix(uds) => {
200 let mut udsargs = vec![];
201
202 udsargs.push(format!("unix:{}", uds.path.display()));
203 if let Some(server) = &uds.server {
204 udsargs.push(format!("server={}", server.to_arg()));
205 }
206 if let Some(wait) = &uds.wait {
207 udsargs.push(format!("wait={}", wait.to_arg()));
208 }
209 if let Some(reconnect_ms) = &uds.reconnect_ms {
210 udsargs.push(format!("reconnect-ms={}", reconnect_ms));
211 }
212 args.push(udsargs.join(","));
213 }
214 SpecialDevice::Mon(mon) => {
215 args.push(format!("mon:{}", mon));
216 }
217 SpecialDevice::Braille => {
218 args.push("braille".to_string());
219 }
220 SpecialDevice::Msmouse => {
221 args.push("msmouse".to_string());
222 }
223 }
224 args
225 }
226}
227
228impl FromStr for SpecialDevice {
229 type Err = String;
230
231 fn from_str(s: &str) -> Result<Self, Self::Err> {
232 if let Some(rest) = s.strip_prefix("vc:") {
233 let (w, h, is_pixel) = if let Some((w, h)) = rest.split_once('x') {
234 if let Some(h) = h.strip_suffix('C') { (w.trim_end_matches('C'), h, false) } else { (w, h, true) }
235 } else {
236 return Err(format!("invalid vc geometry: {rest}"));
237 };
238 return Ok(Self::VC(Some(VC {
239 is_pixel,
240 w: w.parse::<usize>().map_err(|e| e.to_string())?,
241 h: h.parse::<usize>().map_err(|e| e.to_string())?,
242 })));
243 }
244 if s == "vc" {
245 return Ok(Self::VC(None));
246 }
247 if s == "none" {
248 return Ok(Self::None);
249 }
250 if s == "null" {
251 return Ok(Self::Null);
252 }
253 if s == "stdio" {
254 return Ok(Self::Stdio);
255 }
256 if s == "braille" {
257 return Ok(Self::Braille);
258 }
259 if s == "msmouse" {
260 return Ok(Self::Msmouse);
261 }
262 if let Some(chardev) = s.strip_prefix("chardev:") {
263 return Ok(Self::Chardev(chardev.to_string()));
264 }
265 if let Some(mon) = s.strip_prefix("mon:") {
266 return Ok(Self::Mon(mon.to_string()));
267 }
268 if let Some(rest) = s.strip_prefix("udp:") {
269 let mut split = rest.split('@');
270 let remote = split.next().ok_or_else(|| "invalid udp endpoint".to_string())?;
271 let local = split.next();
272 let remote_parts = remote.split(':').collect::<Vec<_>>();
273 let (remote_host, remote_port) = match remote_parts.as_slice() {
274 [port] => (None, port.parse::<u16>().map_err(|e| e.to_string())?),
275 [host, port] => (Some((*host).to_string()), port.parse::<u16>().map_err(|e| e.to_string())?),
276 _ => return Err(format!("invalid udp endpoint: {remote}")),
277 };
278 let src = if let Some(local) = local {
279 let (src_ip, src_port) = local.rsplit_once(':').ok_or_else(|| format!("invalid udp source endpoint: {local}"))?;
280 (Some(src_ip.to_string()), Some(src_port.parse::<u16>().map_err(|e| e.to_string())?))
281 } else {
282 (None, None)
283 };
284 return Ok(Self::Udp(Udp {
285 remote_host,
286 remote_port,
287 src_ip: src.0,
288 src_port: src.1,
289 }));
290 }
291 if let Some(rest) = s.strip_prefix("tcp:") {
292 let mut parts = rest.split(',');
293 let endpoint = parts.next().ok_or_else(|| "invalid tcp endpoint".to_string())?;
294 let (host, port) = endpoint.rsplit_once(':').ok_or_else(|| format!("invalid tcp endpoint: {endpoint}"))?;
295 let mut server = None;
296 let mut wait = None;
297 let mut nodelay = None;
298 let mut reconnect_ms = None;
299 for part in parts {
300 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid tcp option: {part}"))?;
301 match key {
302 "server" => server = Some(value.parse::<OnOff>().map_err(|_| format!("invalid server value: {value}"))?),
303 "wait" => wait = Some(value.parse::<OnOff>().map_err(|_| format!("invalid wait value: {value}"))?),
304 "nodelay" => nodelay = Some(value.parse::<OnOff>().map_err(|_| format!("invalid nodelay value: {value}"))?),
305 "reconnect-ms" => reconnect_ms = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
306 other => return Err(format!("unsupported tcp option: {other}")),
307 }
308 }
309 return Ok(Self::Tcp(Tcp {
310 host: host.to_string(),
311 port: port.parse::<u16>().map_err(|e| e.to_string())?,
312 server,
313 wait,
314 nodelay,
315 reconnect_ms,
316 }));
317 }
318 if let Some(rest) = s.strip_prefix("telnet:") {
319 let mut parts = rest.split(',');
320 let endpoint = parts.next().ok_or_else(|| "invalid telnet endpoint".to_string())?;
321 let (host, port) = endpoint.rsplit_once(':').ok_or_else(|| format!("invalid telnet endpoint: {endpoint}"))?;
322 let mut server = None;
323 let mut wait = None;
324 let mut nodelay = None;
325 for part in parts {
326 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid telnet option: {part}"))?;
327 match key {
328 "server" => server = Some(value.parse::<OnOff>().map_err(|_| format!("invalid server value: {value}"))?),
329 "wait" => wait = Some(value.parse::<OnOff>().map_err(|_| format!("invalid wait value: {value}"))?),
330 "nodelay" => nodelay = Some(value.parse::<OnOff>().map_err(|_| format!("invalid nodelay value: {value}"))?),
331 other => return Err(format!("unsupported telnet option: {other}")),
332 }
333 }
334 return Ok(Self::Telnet(Telnet {
335 host: host.to_string(),
336 port: port.parse::<u16>().map_err(|e| e.to_string())?,
337 server,
338 wait,
339 nodelay,
340 }));
341 }
342 if let Some(rest) = s.strip_prefix("websocket:") {
343 let mut parts = rest.split(',');
344 let endpoint = parts.next().ok_or_else(|| "invalid websocket endpoint".to_string())?;
345 let (host, port) = endpoint.rsplit_once(':').ok_or_else(|| format!("invalid websocket endpoint: {endpoint}"))?;
346 let mut server = None;
347 let mut wait = None;
348 let mut nodelay = None;
349 for part in parts {
350 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid websocket option: {part}"))?;
351 match key {
352 "server" => server = Some(value.parse::<OnOff>().map_err(|_| format!("invalid server value: {value}"))?),
353 "wait" => wait = Some(value.parse::<OnOff>().map_err(|_| format!("invalid wait value: {value}"))?),
354 "nodelay" => nodelay = Some(value.parse::<OnOff>().map_err(|_| format!("invalid nodelay value: {value}"))?),
355 other => return Err(format!("unsupported websocket option: {other}")),
356 }
357 }
358 return Ok(Self::Websocket(Websocket {
359 host: host.to_string(),
360 port: port.parse::<u16>().map_err(|e| e.to_string())?,
361 server,
362 wait,
363 nodelay,
364 }));
365 }
366 if let Some(rest) = s.strip_prefix("unix:") {
367 let mut parts = rest.split(',');
368 let path = PathBuf::from(parts.next().ok_or_else(|| "invalid unix endpoint".to_string())?);
369 let mut server = None;
370 let mut wait = None;
371 let mut reconnect_ms = None;
372 for part in parts {
373 let (key, value) = part.split_once('=').ok_or_else(|| format!("invalid unix option: {part}"))?;
374 match key {
375 "server" => server = Some(value.parse::<OnOff>().map_err(|_| format!("invalid server value: {value}"))?),
376 "wait" => wait = Some(value.parse::<OnOff>().map_err(|_| format!("invalid wait value: {value}"))?),
377 "reconnect-ms" => reconnect_ms = Some(value.parse::<usize>().map_err(|e| e.to_string())?),
378 other => return Err(format!("unsupported unix option: {other}")),
379 }
380 }
381 return Ok(Self::Unix(Unix { path, server, wait, reconnect_ms }));
382 }
383 if let Some(path) = s.strip_prefix("file:") {
384 return Ok(Self::File(PathBuf::from(path)));
385 }
386 if let Some(path) = s.strip_prefix("pipe:") {
387 return Ok(Self::Pipe(PathBuf::from(path)));
388 }
389 if let Some(path) = s.strip_prefix("pty:") {
390 return Ok(Self::Pty(Some(PathBuf::from(path))));
391 }
392 if s == "pty" {
393 return Ok(Self::Pty(None));
394 }
395 if let Some(dev) = s.strip_prefix("/dev/parport") {
396 let index = dev.parse::<usize>().map_err(|e| e.to_string())?;
397 return Ok(Self::Parport(index));
398 }
399 if let Some(dev) = s.strip_prefix("/dev/") {
400 return Ok(Self::Dev(dev.to_string()));
401 }
402 if let Some(port) = s.strip_prefix("COM") {
403 return Ok(Self::Com(port.parse::<usize>().map_err(|e| e.to_string())?));
404 }
405
406 Err(format!("unsupported special device: {s}"))
407 }
408}