1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
use std::collections::HashMap;
use super::LinkAnchor;
const TERMINATOR: &str = "\u{1b}\\";
#[derive(Debug, Clone)]
pub struct LinkHandler {
links: HashMap<u16, Link>,
link_index: u16,
}
#[derive(Debug, Clone)]
pub struct Link {
pub id: Option<String>,
pub uri: String,
}
impl LinkHandler {
pub fn new() -> Self {
Self {
links: HashMap::new(),
link_index: 0,
}
}
pub fn dispatch_osc8(&mut self, params: &[&[u8]]) -> Option<LinkAnchor> {
let (link_params, uri) = (params[1], params[2]);
log::debug!(
"dispatching osc8, params: {:?}, uri: {:?}",
std::str::from_utf8(link_params),
std::str::from_utf8(uri)
);
if !uri.is_empty() {
// save the link, and the id if present to hashmap
String::from_utf8(uri.to_vec()).ok().map(|uri| {
let id = link_params
.split(|&b| b == b':')
.find(|kv| kv.starts_with(b"id="))
.and_then(|kv| String::from_utf8(kv[3..].to_vec()).ok());
let anchor = LinkAnchor::Start(self.link_index);
self.links.insert(self.link_index, Link { id, uri });
self.link_index += 1;
anchor
})
} else {
// there is no link, so consider it a link end
Some(LinkAnchor::End)
}
}
pub fn new_link_from_url(&mut self, url: String) -> LinkAnchor {
let anchor = LinkAnchor::Start(self.link_index);
self.links.insert(
self.link_index,
Link {
id: Some(self.link_index.to_string()),
uri: url,
},
);
self.link_index += 1;
anchor
}
pub fn output_osc8(&self, link_anchor: Option<LinkAnchor>) -> Option<String> {
link_anchor.and_then(|link| match link {
LinkAnchor::Start(index) => {
let link = self.links.get(&index);
let output = link.map(|link| {
let id = link
.id
.as_ref()
.map_or("".to_string(), |id| format!("id={}", id));
format!("\u{1b}]8;{};{}{}", id, link.uri, TERMINATOR)
});
if output.is_none() {
log::warn!(
"attempted to output osc8 link start, but id: {} was not found!",
index
);
}
output
},
LinkAnchor::End => Some(format!("\u{1b}]8;;{}", TERMINATOR)),
})
}
#[cfg(test)]
pub fn links(&self) -> HashMap<u16, Link> {
self.links.clone()
}
}
impl Default for LinkHandler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dispatch_osc8_link_start() {
let mut link_handler = LinkHandler::default();
let link_params = "id=test";
let uri = "http://test.com";
let params = vec!["8".as_bytes(), link_params.as_bytes(), uri.as_bytes()];
let anchor = link_handler.dispatch_osc8(¶ms);
match anchor {
Some(LinkAnchor::Start(link_id)) => {
let link = link_handler.links.get(&link_id).expect("link was not some");
assert_eq!(link.id, Some("test".to_string()));
assert_eq!(link.uri, uri);
},
_ => panic!("pending link handler was not start"),
}
let expected = format!("\u{1b}]8;id=test;http://test.com{}", TERMINATOR);
assert_eq!(link_handler.output_osc8(anchor).unwrap(), expected);
}
#[test]
fn dispatch_osc8_link_end() {
let mut link_handler = LinkHandler::default();
let params: Vec<&[_]> = vec![b"8", b"", b""];
let anchor = link_handler.dispatch_osc8(¶ms);
assert_eq!(anchor, Some(LinkAnchor::End));
let expected = format!("\u{1b}]8;;{}", TERMINATOR);
assert_eq!(link_handler.output_osc8(anchor).unwrap(), expected);
}
#[test]
fn return_none_on_missing_link_id() {
let link_handler = LinkHandler::default();
let anchor = LinkAnchor::Start(100);
assert_eq!(link_handler.output_osc8(Some(anchor)), None);
}
}
|