#![cfg(feature = "std")]
mod connect_instruction;
pub use connect_instruction::ConnectAddress;
use alloc::string::{String, ToString};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedDisplay {
pub host: String,
pub protocol: Option<String>,
pub display: u16,
pub screen: u16,
}
impl ParsedDisplay {
pub fn connect_instruction(&self) -> impl Iterator<Item = ConnectAddress<'_>> {
connect_instruction::connect_addresses(self)
}
}
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(&std::env::var("DISPLAY").ok()?),
}
}
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(|p| p.to_string());
Some(ParsedDisplay {
host,
protocol,
display,
screen,
})
}
#[cfg(test)]
mod test {
use super::{parse_display, ParsedDisplay};
use alloc::string::ToString;
fn do_parse_display(input: &str) -> Option<ParsedDisplay> {
std::env::set_var("DISPLAY", input);
let result1 = parse_display(None);
std::env::remove_var("DISPLAY");
let result2 = parse_display(Some(input));
assert_eq!(result1, result2);
result1
}
#[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), None);
}
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: "".to_string(),
protocol: None,
display: 0,
screen: 0,
},
),
(
":1",
ParsedDisplay {
host: "".to_string(),
protocol: None,
display: 1,
screen: 0,
},
),
(
":0.1",
ParsedDisplay {
host: "".to_string(),
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
);
}
}
}