use super::osc_codes::OscSequence;
use std::path::Path;
#[must_use]
pub fn format_hyperlink(uri: &str, text: &str) -> String {
let start = OscSequence::HyperlinkStart {
uri: uri.to_string(),
id: None,
};
let end = OscSequence::HyperlinkEnd;
format!("{start}{text}{end}")
}
#[must_use]
pub fn format_file_hyperlink(path: &Path) -> String {
let display_text = path.display().to_string();
let uri = if path.is_absolute() {
format!("file://{}", path.display())
} else {
match std::env::current_dir().map(|cwd| cwd.join(path)) {
Ok(abs_path) => format!("file://{}", abs_path.display()),
Err(_) => format!("file://{}", path.display()), }
};
let encoded_uri = uri
.chars()
.map(|c| match c {
' ' => "%20".to_string(),
'#' => "%23".to_string(),
'?' => "%3F".to_string(),
'&' => "%26".to_string(),
'%' => "%25".to_string(),
_ => c.to_string(),
})
.collect::<String>();
format_hyperlink(&encoded_uri, &display_text)
}
#[cfg(test)]
mod tests {
use super::{format_file_hyperlink, format_hyperlink};
use std::path::Path;
#[test]
fn test_format_hyperlink_basic() {
let result = format_hyperlink("https://example.com", "Example Link");
let expected = "\x1b]8;;https://example.com\x07Example Link\x1b]8;;\x07";
assert_eq!(result, expected);
}
#[test]
fn test_format_hyperlink_empty() {
let result = format_hyperlink("", "Empty URI");
let expected = "\x1b]8;;\x07Empty URI\x1b]8;;\x07";
assert_eq!(result, expected);
}
#[test]
fn test_format_hyperlink_special_chars() {
let result =
format_hyperlink("https://example.com/path?q=test&v=1", "Complex URL");
let expected =
"\x1b]8;;https://example.com/path?q=test&v=1\x07Complex URL\x1b]8;;\x07";
assert_eq!(result, expected);
}
#[test]
fn test_format_file_hyperlink_absolute_path() {
let path = Path::new("/home/user/document.txt");
let result = format_file_hyperlink(path);
assert!(result.contains("file:///home/user/document.txt"));
assert!(result.contains("/home/user/document.txt"));
assert!(result.starts_with("\x1b]8;;"));
assert!(result.ends_with("\x1b]8;;\x07"));
}
#[test]
fn test_format_file_hyperlink_with_spaces() {
let path = Path::new("/home/user/my document.txt");
let result = format_file_hyperlink(path);
assert!(result.contains("file:///home/user/my%20document.txt"));
assert!(result.contains("/home/user/my document.txt"));
}
#[test]
fn test_format_file_hyperlink_with_special_chars() {
let path = Path::new("/home/user/file#with&special%chars?.txt");
let result = format_file_hyperlink(path);
assert!(
result.contains("file:///home/user/file%23with%26special%25chars%3F.txt")
);
assert!(result.contains("/home/user/file#with&special%chars?.txt"));
}
#[test]
fn test_format_file_hyperlink_relative_path() {
let path = Path::new("./relative/path.txt");
let result = format_file_hyperlink(path);
assert!(result.starts_with("\x1b]8;;file://"));
assert!(result.contains("./relative/path.txt"));
}
#[test]
fn test_url_encoding_coverage() {
let test_cases = [
(" ", "%20"),
("#", "%23"),
("?", "%3F"),
("&", "%26"),
("%", "%25"),
];
for (input_char, expected_encoding) in test_cases {
let path_str = format!("/home/user/file{input_char}test.txt");
let path = Path::new(&path_str);
let result = format_file_hyperlink(path);
assert!(
result.contains(expected_encoding),
"Failed to encode '{input_char}' as '{expected_encoding}' in result: {result}"
);
}
}
}