use alloc::string::{String, ToString};
use rusl::string::unix_str::{UnixStr, UnixString};
use xcb_rust_protocol::XcbEnv;
mod connect_instruction;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedDisplay {
pub host: String,
pub protocol: Option<String>,
pub display: u16,
pub screen: u16,
}
impl ParsedDisplay {
#[must_use]
pub fn connect_instruction(&self) -> Option<UnixString> {
connect_instruction::connect_addresses(self)
}
}
#[must_use]
pub fn parse_display(dpy_name: Option<&str>) -> Option<ParsedDisplay> {
match dpy_name {
Some(dpy_name) => parse_display_impl(dpy_name),
None => parse_display_impl(":0"),
}
}
fn parse_display_impl(dpy_name: &str) -> Option<ParsedDisplay> {
let (protocol, remaining) = if let Some(pos) = dpy_name.rfind('/') {
(Some(&dpy_name[..pos]), &dpy_name[pos + 1..])
} else {
(None, dpy_name)
};
let pos = remaining.rfind(':')?;
let (host, remaining) = (&remaining[..pos], &remaining[pos + 1..]);
let (display, screen) = match remaining.find('.') {
Some(pos) => (&remaining[..pos], &remaining[pos + 1..]),
None => (remaining, "0"),
};
let (display, screen) = (display.parse().ok()?, screen.parse().ok()?);
let host = host.to_string();
let protocol = protocol.map(alloc::string::ToString::to_string);
Some(ParsedDisplay {
host,
protocol,
display,
screen,
})
}
#[cfg(test)]
mod test {
use super::*;
fn do_parse_display(input: &str) -> Option<ParsedDisplay> {
parse_display(Some(input))
}
#[test]
fn test_parsing() {
test_missing_input();
xcb_good_cases();
xcb_bad_cases();
own_good_cases();
}
fn test_missing_input() {
std::env::remove_var("DISPLAY");
assert_eq!(
parse_display(None),
Some(ParsedDisplay {
host: String::new(),
protocol: None,
display: 0,
screen: 0
})
);
}
fn own_good_cases() {
for (input, output) in &[
(
"foo/bar:1",
ParsedDisplay {
host: "bar".to_string(),
protocol: Some("foo".to_string()),
display: 1,
screen: 0,
},
),
(
"foo/bar:1.2",
ParsedDisplay {
host: "bar".to_string(),
protocol: Some("foo".to_string()),
display: 1,
screen: 2,
},
),
(
"a:b/c/foo:bar:1.2",
ParsedDisplay {
host: "foo:bar".to_string(),
protocol: Some("a:b/c".to_string()),
display: 1,
screen: 2,
},
),
] {
assert_eq!(
do_parse_display(input).as_ref(),
Some(output),
"Failed parsing correctly: {input}"
);
}
}
fn xcb_good_cases() {
for (input, output) in &[
(
":0",
ParsedDisplay {
host: String::new(),
protocol: None,
display: 0,
screen: 0,
},
),
(
":1",
ParsedDisplay {
host: String::new(),
protocol: None,
display: 1,
screen: 0,
},
),
(
":0.1",
ParsedDisplay {
host: String::new(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"x.org:0",
ParsedDisplay {
host: "x.org".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"expo:0",
ParsedDisplay {
host: "expo".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"bigmachine:1",
ParsedDisplay {
host: "bigmachine".to_string(),
protocol: None,
display: 1,
screen: 0,
},
),
(
"hydra:0.1",
ParsedDisplay {
host: "hydra".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"198.112.45.11:0",
ParsedDisplay {
host: "198.112.45.11".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"198.112.45.11:0.1",
ParsedDisplay {
host: "198.112.45.11".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
":::0",
ParsedDisplay {
host: "::".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"1:::0",
ParsedDisplay {
host: "1::".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"::1:0",
ParsedDisplay {
host: "::1".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"::1:0.1",
ParsedDisplay {
host: "::1".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"::127.0.0.1:0",
ParsedDisplay {
host: "::127.0.0.1".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"::ffff:127.0.0.1:0",
ParsedDisplay {
host: "::ffff:127.0.0.1".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"2002:83fc:3052::1:0",
ParsedDisplay {
host: "2002:83fc:3052::1".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"2002:83fc:3052::1:0.1",
ParsedDisplay {
host: "2002:83fc:3052::1".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"[::]:0",
ParsedDisplay {
host: "[::]".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"[1::]:0",
ParsedDisplay {
host: "[1::]".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"[::1]:0",
ParsedDisplay {
host: "[::1]".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"[::1]:0.1",
ParsedDisplay {
host: "[::1]".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"[::127.0.0.1]:0",
ParsedDisplay {
host: "[::127.0.0.1]".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"[2002:83fc:d052::1]:0",
ParsedDisplay {
host: "[2002:83fc:d052::1]".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"[2002:83fc:d052::1]:0.1",
ParsedDisplay {
host: "[2002:83fc:d052::1]".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
(
"myws::0",
ParsedDisplay {
host: "myws:".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"big::0",
ParsedDisplay {
host: "big:".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
"hydra::0.1",
ParsedDisplay {
host: "hydra:".to_string(),
protocol: None,
display: 0,
screen: 1,
},
),
] {
assert_eq!(
do_parse_display(input).as_ref(),
Some(output),
"Failed parsing correctly: {input}"
);
}
}
fn xcb_bad_cases() {
for input in &[
"",
":",
"::",
":::",
":.",
":a",
":a.",
":0.",
":.a",
":.0",
":0.a",
":0.0.",
"127.0.0.1",
"127.0.0.1:",
"127.0.0.1::",
"::127.0.0.1",
"::127.0.0.1:",
"::127.0.0.1::",
"::ffff:127.0.0.1",
"::ffff:127.0.0.1:",
"::ffff:127.0.0.1::",
"localhost",
"localhost:",
"localhost::",
] {
assert_eq!(
do_parse_display(input),
None,
"Unexpectedly parsed: {input}"
);
}
}
}