1//! Provides the `ConnectInstruction` structure, which allows for a `ParsedDisplay`
2//! to be transformed into a server connection.
3
4use super::ParsedDisplay;
5use alloc::format;
6use alloc::string::String;
7use alloc::vec::Vec;
8
9/// A possible address for an X11 server.
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[non_exhaustive]
12pub 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`.
24pub(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)]
58mod 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