x11rb_protocol/parse_display/
connect_instruction.rs

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}