Sweep every first-party crate source (1956 .rs files) to the project comment guidelines: delete redundant restatements, decorative banners, change narration, and end-of-line comments; keep and tighten the crucial ones (invariants, bug rationale, SAFETY blocks, ported-source attribution). No functional code changed. Every edit is proven comment-only against the prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a separate doctest-fence check. Where removing a comment made rustfmt or clippy want to re-lay-out adjacent code, the minimal triggering comment is restored so code tokens stay byte-identical. Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy --workspace --all-targets (0 warnings). Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for these guidelines (flags banners, end-of-line comments, change narration, and commented-out code).
197 lines
6.9 KiB
Rust
197 lines
6.9 KiB
Rust
//! REAL host-clipboard plumbing shared by the OS-native paste e2e tests and
|
|
//! the `paste_latency` bench: `pbcopy` / `pbpaste` / `osascript` on macOS,
|
|
//! PowerShell `Set-Clipboard` / `Get-Clipboard` / WinForms `SetImage` on
|
|
//! Windows.
|
|
//!
|
|
//! Everything here mutates or reads the MACHINE-GLOBAL clipboard, so callers
|
|
//! must serialize against each other (e.g. `#[serial_test::serial]`) and
|
|
//! should hold a [`HostClipboardTextGuard`] to restore the prior text. CI
|
|
//! sessions without a usable clipboard are detected via
|
|
//! [`clipboard_roundtrip_works`] so tests can skip instead of fail.
|
|
//!
|
|
//! Compiles on every platform so cross-platform builds of the consumers stay
|
|
//! green (the bench gates at runtime); on unsupported hosts the tool spawns
|
|
//! simply fail.
|
|
|
|
use std::io::Write as _;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Stdio};
|
|
|
|
use anyhow::{Context, Result, bail};
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn pbcopy(text: &str) -> Result<()> {
|
|
let mut cmd = Command::new("pbcopy");
|
|
cmd.stdin(Stdio::piped());
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let mut child = cmd.spawn().context("spawn pbcopy")?;
|
|
child
|
|
.stdin
|
|
.take()
|
|
.context("pbcopy stdin")?
|
|
.write_all(text.as_bytes())
|
|
.context("write pbcopy stdin")?;
|
|
let status = child.wait().context("wait pbcopy")?;
|
|
if !status.success() {
|
|
bail!("pbcopy exited with {status}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Copy `text` to the host clipboard via PowerShell `Set-Clipboard`.
|
|
#[cfg(target_os = "windows")]
|
|
pub fn pbcopy(text: &str) -> Result<()> {
|
|
// Text travels over stdin, never inside the command line — no quoting.
|
|
let mut cmd = Command::new("powershell");
|
|
cmd.args([
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-Command",
|
|
"Set-Clipboard -Value ([Console]::In.ReadToEnd())",
|
|
])
|
|
.stdin(Stdio::piped());
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let mut child = cmd.spawn().context("spawn powershell Set-Clipboard")?;
|
|
child
|
|
.stdin
|
|
.take()
|
|
.context("powershell stdin")?
|
|
.write_all(text.as_bytes())
|
|
.context("write powershell stdin")?;
|
|
let status = child.wait().context("wait powershell Set-Clipboard")?;
|
|
if !status.success() {
|
|
bail!("powershell Set-Clipboard exited with {status}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Current host clipboard TEXT via `pbpaste` (`None` when unavailable).
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn pbpaste() -> Option<String> {
|
|
let mut cmd = Command::new("pbpaste");
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let out = cmd.output().ok()?;
|
|
if !out.status.success() {
|
|
return None;
|
|
}
|
|
Some(String::from_utf8_lossy(&out.stdout).into_owned())
|
|
}
|
|
|
|
/// Current host clipboard TEXT via PowerShell `Get-Clipboard` (`None` when
|
|
/// unavailable).
|
|
#[cfg(target_os = "windows")]
|
|
pub fn pbpaste() -> Option<String> {
|
|
let mut cmd = Command::new("powershell");
|
|
// Console::Out.Write avoids the trailing newline PowerShell's pipeline
|
|
// output would append (roundtrip checks compare exact text).
|
|
cmd.args([
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-Command",
|
|
"$c = Get-Clipboard -Raw; if ($null -ne $c) { [Console]::Out.Write($c) }",
|
|
]);
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let out = cmd.output().ok()?;
|
|
if !out.status.success() {
|
|
return None;
|
|
}
|
|
Some(String::from_utf8_lossy(&out.stdout).into_owned())
|
|
}
|
|
|
|
/// Put the PNG at `path` on the host clipboard as a raster (`«class PNGf»`).
|
|
#[cfg(not(target_os = "windows"))]
|
|
pub fn set_clipboard_png(path: &Path) -> Result<()> {
|
|
let script = format!(
|
|
"set the clipboard to (read (POSIX file \"{}\") as «class PNGf»)",
|
|
path.display()
|
|
);
|
|
let mut cmd = Command::new("osascript");
|
|
cmd.arg("-e").arg(&script);
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let status = cmd.status().context("spawn osascript")?;
|
|
if !status.success() {
|
|
bail!("osascript set-clipboard-PNG exited with {status}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Put the PNG at `path` on the host clipboard as a raster via a WinForms
|
|
/// `Clipboard::SetImage` PowerShell one-liner.
|
|
#[cfg(target_os = "windows")]
|
|
pub fn set_clipboard_png(path: &Path) -> Result<()> {
|
|
use base64::Engine as _;
|
|
// WinForms Clipboard requires an STA thread; -EncodedCommand (base64 of
|
|
// UTF-16LE) sidesteps every cmd/PowerShell quoting layer, leaving only
|
|
// the PS single-quote escape for the embedded path.
|
|
let ps_path = path.display().to_string().replace('\'', "''");
|
|
let script = format!(
|
|
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing; \
|
|
$img = [System.Drawing.Image]::FromFile('{ps_path}'); \
|
|
[System.Windows.Forms.Clipboard]::SetImage($img); \
|
|
$img.Dispose()"
|
|
);
|
|
let utf16: Vec<u8> = script
|
|
.encode_utf16()
|
|
.flat_map(|u| u.to_le_bytes())
|
|
.collect();
|
|
let encoded = base64::engine::general_purpose::STANDARD.encode(utf16);
|
|
let mut cmd = Command::new("powershell");
|
|
cmd.args([
|
|
"-NoProfile",
|
|
"-NonInteractive",
|
|
"-STA",
|
|
"-EncodedCommand",
|
|
&encoded,
|
|
]);
|
|
kigi_tty_utils::detach_std_command(&mut cmd);
|
|
let status = cmd.status().context("spawn powershell SetImage")?;
|
|
if !status.success() {
|
|
bail!("powershell SetImage exited with {status}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn write_fixture_png(dir: &Path) -> Result<PathBuf> {
|
|
let path = dir.join("host_clipboard_fixture.png");
|
|
let buf: image::ImageBuffer<image::Rgba<u8>, Vec<u8>> =
|
|
image::ImageBuffer::from_pixel(64, 64, image::Rgba([200, 40, 120, 255]));
|
|
buf.save(&path)
|
|
.context("write host clipboard fixture png")?;
|
|
Ok(path)
|
|
}
|
|
|
|
/// Whether the host clipboard actually works in this session: sets a nonce
|
|
/// via the text helper and reads it back. False on clipboard-less CI sessions
|
|
/// (e.g. a Windows service session with no interactive desktop) so tests can
|
|
/// SKIP loudly instead of failing on environment.
|
|
pub fn clipboard_roundtrip_works() -> bool {
|
|
let nonce = format!("HOSTCLIPROUNDTRIP{}", std::process::id());
|
|
if pbcopy(&nonce).is_err() {
|
|
return false;
|
|
}
|
|
// trim_end: some clipboard tool chains append a trailing newline.
|
|
pbpaste().is_some_and(|t| t.trim_end() == nonce)
|
|
}
|
|
|
|
/// Best-effort save/restore of the host TEXT clipboard around a test or bench
|
|
/// run. Restores on drop (panic/unwind included). A prior IMAGE clipboard
|
|
/// cannot be restored — `pbpaste` only reads the text representation.
|
|
pub struct HostClipboardTextGuard {
|
|
prior: Option<String>,
|
|
}
|
|
|
|
impl HostClipboardTextGuard {
|
|
pub fn save() -> Self {
|
|
Self { prior: pbpaste() }
|
|
}
|
|
}
|
|
|
|
impl Drop for HostClipboardTextGuard {
|
|
fn drop(&mut self) {
|
|
if let Some(prior) = self.prior.take() {
|
|
// Non-panicking restore: a panicking test must still unwind cleanly.
|
|
let _ = pbcopy(&prior);
|
|
}
|
|
}
|
|
}
|