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::vec::Vec;
7use std::path::PathBuf;
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(PathBuf),
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.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)]
58mod 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