use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::Mutex;
static LAST_EXPORT: Mutex<Option<(u64, bool)>> = Mutex::new(None);
fn hash_of(text: &str) -> u64 {
let mut h = DefaultHasher::new();
text.hash(&mut h);
h.finish()
}
pub(crate) fn export_eol(text: &str) -> String {
if cfg!(windows) {
text.replace('\n', "\r\n")
} else {
text.to_owned()
}
}
pub(crate) fn record(exported: &str, is_entire_line: bool) {
*LAST_EXPORT.lock().expect("clipboard table lock") = Some((hash_of(exported), is_entire_line));
}
pub(crate) fn is_entire_line(pasted: &str) -> bool {
LAST_EXPORT.lock().expect("clipboard table lock").is_some_and(|(h, entire)| entire && h == hash_of(pasted))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_matches_and_external_text_misses() {
let exported = export_eol("two\n");
record(&exported, true);
assert!(is_entire_line(&exported), "our own export matches");
assert!(!is_entire_line("something else"), "external text degrades to plain paste");
record("plain", false);
assert!(!is_entire_line(&exported), "stale entry no longer matches");
assert!(!is_entire_line("plain"), "and the new one is not entire-line");
}
#[test]
fn export_eol_is_platform_flavored() {
let out = export_eol("a\nb\n");
if cfg!(windows) {
assert_eq!(out, "a\r\nb\r\n");
} else {
assert_eq!(out, "a\nb\n");
}
}
}