//! 把 UTF-8 文本读写适配到当前平台的系统剪贴板命令。 //! 外部命令失败作为普通错误交给调用方处理;这里不解析或执行剪贴板中的程序文本。 //! //! Clipboard text is always UTF-8, including Finder/LaunchServices launches without LANG. use anyhow::{Result, bail}; #[cfg(target_os = "macos")] /// 构造固定 UTF-8 环境的系统命令;本函数不启动进程,退出状态由 read/write 检查。 // 仅 macOS 的 read/write 使用,构造独立 Command 并覆盖编码环境,不修改宿主进程全局环境变量。 fn command(program: &str) -> std::process::Command { let mut cmd = std::process::Command::new(program); // 从 Finder 等无 LANG 的环境启动时也显式指定 UTF-8,防止系统工具按其他编码转换中文。 cmd.env("LC_ALL", "en_US.UTF-8") .env("LC_CTYPE", "en_US.UTF-8") .env("LANG", "en_US.UTF-8"); cmd } /// 取得系统剪贴板的 UTF-8 文本,失败作为可显示错误返回。 // shell/app 处理开发工具粘贴快捷键时调用;失败由工具 input_error 展示,不用空字符串冒充成功。 pub fn read() -> Result { #[cfg(target_os = "macos")] { // 直接启动固定路径程序并传递独立参数,不经过 shell;-Prefer txt 要求文本表示。 let output = command("/usr/bin/pbpaste") .arg("-Prefer") .arg("txt") .output()?; // 进程正常退出但状态非成功仍为错误,不能把可能残缺的 stdout 当作源代码粘贴。 if !output.status.success() { bail!("system clipboard read failed"); } // Never silently replace invalid bytes in the user's source. // 严格验证输出 UTF-8,非法字节报错而不是替换为 U+FFFD 后悄悄改变用户文本。 Ok(String::from_utf8(output.stdout)?) } // 平台分支在编译时选择,非 macOS 不尝试猜测其他系统剪贴板命令。 #[cfg(not(target_os = "macos"))] { bail!("system clipboard adapter is not available on this platform") } } /// 将提供的文本交给系统剪贴板命令,不执行文本内容。 // shell/app 的复制与剪切路径调用;只有成功返回后剪切才会删除编辑器中的选中文本。 pub fn write(value: &str) -> Result<()> { #[cfg(target_os = "macos")] { use std::io::Write; // 正文只经子进程标准输入传递,不拼成命令参数,因此引号、换行和源码中的命令字符保持原意。 let mut child = command("/usr/bin/pbcopy") .stdin(std::process::Stdio::piped()) .spawn()?; // 取走子进程管道句柄,后面可显式 drop 发送 EOF,不能让 pbcopy 一直等待更多输入。 let mut stdin = child .stdin .take() .ok_or_else(|| anyhow::anyhow!("clipboard stdin is unavailable"))?; // 按 UTF-8 原字节写完整内容;写管道失败立即传播,此函数该错误路径没有另外执行 wait。 stdin.write_all(value.as_bytes())?; // 写完后先关闭管道发送 EOF,让 pbcopy 知道输入结束;之后等待退出状态,不能仅以写管道成功作为完成。 drop(stdin); // 等待真实系统工具退出并检查状态;写入成功和剪贴板操作完成是两个独立检查。 if !child.wait()?.success() { bail!("system clipboard write failed"); } Ok(()) } #[cfg(not(target_os = "macos"))] { // 非 macOS 当前没有实现适配器,显式报错而不伪装成写入成功。 let _ = value; bail!("system clipboard adapter is not available on this platform") } }