winprint_ext/test_utils/
null_device.rs1use crate::printer::PrinterDevice;
2use sha2::{Digest, Sha256};
3use std::{cell::OnceCell, process::Stdio, sync::OnceLock};
4
5thread_local! {
6 static NULL_DEVICE: OnceCell<NullPrinterDevice> = const { OnceCell::new() };
7}
8static DEVICE_ID_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
9static PORT_AND_DRIVER_INSTALLER: OnceLock<()> = OnceLock::new();
10
11struct NullPrinterDevice {
12 printer: PrinterDevice,
13}
14
15impl NullPrinterDevice {
16 fn new() -> Self {
17 PORT_AND_DRIVER_INSTALLER.get_or_init(|| {
18 std::process::Command::new("powershell")
19 .stdin(Stdio::null())
20 .stdout(Stdio::inherit())
21 .stderr(Stdio::inherit())
22 .args([
23 "-Command",
24 "if (-not (Get-PrinterDriver -Name 'Generic / Text Only' -ErrorAction SilentlyContinue)) {Add-PrinterDriver 'Generic / Text Only' -ErrorAction Continue} if (-not (Get-PrinterPort -Name 'nul:' -ErrorAction SilentlyContinue)) {Add-PrinterPort -Name 'nul:' -ErrorAction Continue}",
25 ])
26 .spawn()
27 .unwrap()
28 .wait()
29 .unwrap();
30 });
31
32 let id = DEVICE_ID_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
33
34 let exe_path = std::env::current_exe().unwrap().canonicalize().unwrap();
35 let exe_id =
36 bs58::encode(Sha256::digest(exe_path.to_string_lossy().as_bytes())).into_string();
37
38 let printer_name = format!("null-device-{}-{}", exe_id, id);
39
40 let printer = PrinterDevice::all()
41 .unwrap()
42 .into_iter()
43 .find(|p| p.name() == printer_name);
44 if let Some(printer) = printer {
45 return NullPrinterDevice { printer };
46 }
47
48 std::process::Command::new("powershell")
49 .stdin(Stdio::null())
50 .stdout(Stdio::inherit())
51 .stderr(Stdio::inherit())
52 .args([
53 "-Command",
54 &format!(
55 "Add-Printer -Name '{}' -PortName 'nul:' -DriverName 'Generic / Text Only'",
56 printer_name
57 ),
58 ])
59 .spawn()
60 .unwrap()
61 .wait()
62 .unwrap();
63
64 let printer = PrinterDevice::all()
65 .unwrap()
66 .into_iter()
67 .find(|p| p.name() == printer_name)
68 .unwrap();
69 NullPrinterDevice { printer }
70 }
71}
72
73impl Drop for NullPrinterDevice {
74 fn drop(&mut self) {
75 std::process::Command::new("powershell")
76 .stdin(Stdio::null())
77 .stdout(Stdio::inherit())
78 .stderr(Stdio::inherit())
79 .args([
80 "-Command",
81 &format!("Remove-Printer -Name '{}'", self.printer.name()),
82 ])
83 .spawn()
84 .unwrap()
85 .wait()
86 .unwrap();
87 }
88}
89
90pub fn thread_local() -> PrinterDevice {
93 NULL_DEVICE.with(|f| f.get_or_init(NullPrinterDevice::new).printer.clone())
94}