1 | //! Provides the `ConnectInstruction` structure, which allows for a `ParsedDisplay` |
2 | //! to be transformed into a server connection. |
3 | |
4 | use super::ParsedDisplay; |
5 | use alloc::format; |
6 | use alloc::vec::Vec; |
7 | use std::path::PathBuf; |
8 | |
9 | /// A possible address for an X11 server. |
10 | #[derive (Debug, Clone, PartialEq, Eq)] |
11 | #[non_exhaustive ] |
12 | pub enum ConnectAddress<'a> { |
13 | /// Connect to this hostname and port over TCP. |
14 | Hostname(&'a str, u16), |
15 | /// Connect to this Unix socket. |
16 | /// |
17 | /// First, the given path should be attempted in the abstract namespace. Only if that fails, |
18 | /// then the named socket with the given name should be tried. |
19 | Socket(PathBuf), |
20 | } |
21 | |
22 | /// Get an iterator over all of the addresses we should target with a |
23 | /// `ParsedDisplay`. |
24 | pub(super) fn connect_addresses(p: &ParsedDisplay) -> impl Iterator<Item = ConnectAddress<'_>> { |
25 | const TCP_PORT_BASE: u16 = 6000; |
26 | let ParsedDisplay { |
27 | host, |
28 | protocol, |
29 | display, |
30 | .. |
31 | } = p; |
32 | |
33 | let mut targets = Vec::new(); |
34 | |
35 | if (protocol.is_none() || protocol.as_deref() != Some("unix" )) |
36 | && !host.is_empty() |
37 | && host != "unix" |
38 | { |
39 | targets.push(ConnectAddress::Hostname(host, TCP_PORT_BASE + display)); |
40 | } else { |
41 | if protocol.is_none() || protocol.as_deref() == Some("unix" ) { |
42 | let file_name = format!("/tmp/.X11-unix/X {}" , display); |
43 | targets.push(ConnectAddress::Socket(file_name.into())); |
44 | } |
45 | |
46 | if protocol.is_none() && host.is_empty() { |
47 | targets.push(ConnectAddress::Hostname( |
48 | "localhost" , |
49 | TCP_PORT_BASE + display, |
50 | )); |
51 | } |
52 | } |
53 | |
54 | targets.into_iter() |
55 | } |
56 | |
57 | #[cfg (test)] |
58 | mod tests { |
59 | // make sure iterator properties are clean |
60 | use super::{super::parse_display, ConnectAddress}; |
61 | use alloc::{vec, vec::Vec}; |
62 | use std::path::PathBuf; |
63 | |
64 | #[test ] |
65 | fn basic_test() { |
66 | let pd = parse_display(Some(":0" )).unwrap(); |
67 | let ci = pd.connect_instruction(); |
68 | let ci = ci.collect::<Vec<_>>(); |
69 | |
70 | assert_eq!( |
71 | ci, |
72 | vec![ |
73 | ConnectAddress::Socket(PathBuf::from("/tmp/.X11-unix/X0" )), |
74 | ConnectAddress::Hostname("localhost" , 6000), |
75 | ] |
76 | ); |
77 | } |
78 | |
79 | #[test ] |
80 | fn try_over_hostname() { |
81 | let pd = parse_display(Some("192.168.1.111:0" )).unwrap(); |
82 | let ci = pd.connect_instruction(); |
83 | |
84 | let ci = ci.collect::<Vec<_>>(); |
85 | |
86 | assert_eq!(ci, vec![ConnectAddress::Hostname("192.168.1.111" , 6000),]); |
87 | } |
88 | |
89 | #[test ] |
90 | fn try_over_unix_hostname() { |
91 | let pd = parse_display(Some("unix/host:0" )).unwrap(); |
92 | let ci = pd.connect_instruction(); |
93 | |
94 | let ci = ci.collect::<Vec<_>>(); |
95 | |
96 | assert_eq!( |
97 | ci, |
98 | vec![ConnectAddress::Socket(PathBuf::from("/tmp/.X11-unix/X0" )),] |
99 | ); |
100 | } |
101 | } |
102 | |