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::string::String; |
7 | use alloc::vec::Vec; |
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(String), |
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)); |
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 | |
63 | #[test ] |
64 | fn basic_test() { |
65 | let pd = parse_display(Some(":0" )).unwrap(); |
66 | let ci = pd.connect_instruction(); |
67 | let ci = ci.collect::<Vec<_>>(); |
68 | |
69 | assert_eq!( |
70 | ci, |
71 | vec![ |
72 | ConnectAddress::Socket("/tmp/.X11-unix/X0" .into()), |
73 | ConnectAddress::Hostname("localhost" , 6000), |
74 | ] |
75 | ); |
76 | } |
77 | |
78 | #[test ] |
79 | fn try_over_hostname() { |
80 | let pd = parse_display(Some("192.168.1.111:0" )).unwrap(); |
81 | let ci = pd.connect_instruction(); |
82 | |
83 | let ci = ci.collect::<Vec<_>>(); |
84 | |
85 | assert_eq!(ci, vec![ConnectAddress::Hostname("192.168.1.111" , 6000),]); |
86 | } |
87 | |
88 | #[test ] |
89 | fn try_over_unix_hostname() { |
90 | let pd = parse_display(Some("unix/host:0" )).unwrap(); |
91 | let ci = pd.connect_instruction(); |
92 | |
93 | let ci = ci.collect::<Vec<_>>(); |
94 | |
95 | assert_eq!(ci, vec![ConnectAddress::Socket("/tmp/.X11-unix/X0" .into())]); |
96 | } |
97 | } |
98 | |