use std::env::var;
use std::process::Command;
use arboard::Clipboard;
use log::{debug, error, info};
use crate::error::{Error, Result};
use crate::response::Response;
pub fn handle_clipboard() -> bool {
let cmd = var("ALFRUSCO_COMMAND").ok();
let title = var("TITLE").ok();
let url = var("URL").ok();
if let Some(cmd) = cmd {
debug!("ALFRUSCO_COMMAND provided: {cmd}");
if cmd == "richtext" || cmd == "markdown" {
if let (Some(title), Some(url)) = (title, url) {
let result = if cmd == "richtext" {
copy_rich_text_link_to_clipboard(title, url)
} else {
copy_markdown_link_to_clipboard(title, url)
};
if let Err(e) = result {
error!("Clipboard operation failed: {e}");
}
if let Err(e) = Response::new().write(std::io::stdout()) {
error!("Error writing response: {e}");
}
return true;
}
}
}
false
}
pub fn format_markdown_link(title: impl Into<String>, url: impl Into<String>) -> String {
let title = title.into();
let url = url.into();
format!("[{title}]({url})")
}
pub fn format_html_link(title: impl Into<String>, url: impl Into<String>) -> String {
let title = title.into();
let url = url.into();
format!("<a href=\"{url}\">{title}</a>")
}
pub fn copy_markdown_link_to_clipboard(
title: impl Into<String>,
url: impl Into<String>,
) -> Result<()> {
let markdown = format_markdown_link(title, url);
let mut ctx = Clipboard::new()
.map_err(|e| Error::Clipboard(format!("Failed to initialize clipboard: {e}")))?;
ctx.set_text(&markdown)
.map_err(|e| Error::Clipboard(format!("Failed to set clipboard text: {e}")))?;
info!("Wrote Markdown link to clipboard: {markdown}");
Ok(())
}
pub fn copy_rich_text_link_to_clipboard(
title: impl Into<String>,
url: impl Into<String>,
) -> Result<()> {
let url = url.into();
let html = format_html_link(title, &url);
let apple_script = format!(
"set the clipboard to {{text:\"{}\", «class HTML»:«data HTML{}»}}",
url,
hex::encode(html.as_bytes()),
);
let output = Command::new("osascript")
.arg("-e")
.arg(&apple_script)
.output()
.map_err(|e| Error::Clipboard(format!("Failed to execute osascript: {e}")))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(Error::Clipboard(format!("osascript failed: {stderr}")));
}
info!("Wrote rich text link to clipboard: {html}");
Ok(())
}
#[cfg(test)]
mod tests {
use tempfile::tempdir;
use super::*;
use crate::config::TestingProvider;
use crate::logging::init_logging;
fn initialize() {
let temp_dir = tempdir().unwrap();
let provider = TestingProvider(temp_dir.path().to_path_buf());
let _ = init_logging(&provider);
}
fn cleanup_env_vars() {
std::env::remove_var("ALFRUSCO_COMMAND");
std::env::remove_var("TITLE");
std::env::remove_var("URL");
}
#[test]
fn test_handle_clipboard_markdown() {
initialize();
cleanup_env_vars();
std::env::set_var("ALFRUSCO_COMMAND", "markdown");
std::env::set_var("TITLE", "Test Title");
std::env::set_var("URL", "https://example.com");
let result = handle_clipboard();
assert!(
result,
"handle_clipboard should return true for markdown command"
);
cleanup_env_vars();
}
#[test]
fn test_handle_clipboard_richtext() {
initialize();
cleanup_env_vars();
std::env::set_var("ALFRUSCO_COMMAND", "richtext");
std::env::set_var("TITLE", "Test Title");
std::env::set_var("URL", "https://example.com");
let result = handle_clipboard();
assert!(
result,
"handle_clipboard should return true for richtext command"
);
cleanup_env_vars();
}
#[test]
fn test_handle_clipboard_missing_params() {
initialize();
cleanup_env_vars();
std::env::set_var("ALFRUSCO_COMMAND", "markdown");
let result = handle_clipboard();
assert!(
!result,
"handle_clipboard should return false for missing params"
);
cleanup_env_vars();
}
#[test]
fn test_handle_clipboard_unknown_command() {
initialize();
cleanup_env_vars();
std::env::set_var("ALFRUSCO_COMMAND", "unknown");
std::env::set_var("TITLE", "Test Title");
std::env::set_var("URL", "https://example.com");
let result = handle_clipboard();
assert!(
!result,
"handle_clipboard should return false for unknown commands"
);
cleanup_env_vars();
}
#[test]
fn test_format_markdown_link() {
assert_eq!(
format_markdown_link("Test Title", "https://example.com"),
"[Test Title](https://example.com)"
);
assert_eq!(
format_markdown_link(
"Title [with] brackets",
"https://example.com/path?q=test&p=1"
),
"[Title [with] brackets](https://example.com/path?q=test&p=1)"
);
assert_eq!(format_markdown_link("", ""), "[]()");
}
#[test]
fn test_format_html_link() {
assert_eq!(
format_html_link("Test Title", "https://example.com"),
"<a href=\"https://example.com\">Test Title</a>"
);
assert_eq!(
format_html_link("Title <with> HTML", "https://example.com/path?q=test&p=1"),
"<a href=\"https://example.com/path?q=test&p=1\">Title <with> HTML</a>"
);
assert_eq!(format_html_link("", ""), "<a href=\"\"></a>");
}
#[test]
fn test_markdown_link_formatting() {
initialize();
let result = copy_markdown_link_to_clipboard("Test", "https://example.com");
assert!(result.is_ok(), "Basic markdown link should succeed");
let result = copy_markdown_link_to_clipboard(
"Title [with] brackets",
"https://example.com/path?q=test&p=1",
);
assert!(
result.is_ok(),
"Markdown link with special characters should succeed"
);
}
#[test]
fn test_rich_text_link_formatting() {
initialize();
let result = copy_rich_text_link_to_clipboard("Test", "https://example.com");
assert!(result.is_ok(), "Basic rich text link should succeed");
let result = copy_rich_text_link_to_clipboard(
"Title <with> HTML",
"https://example.com/path?q=test&p=1",
);
assert!(
result.is_ok(),
"Rich text link with special characters should succeed"
);
}
}