Printer Event Handler
A cross-platform Rust library for monitoring printer status and events on Windows and Linux systems.
Features
- Cross-platform - Windows (WMI) and Linux (CUPS) with a single async API.
- Fluent builder -
MonitorBuildercollapses interval/cancellation/property-filter/event-mode options behind one chainable entry point. - Real-time monitoring - millisecond polling, or sub-second event-driven monitoring via the optional
eventscargo feature (WMI__InstanceModificationEventon Windows,org.cups.cupsd.NotifierD-Bus signals on Linux). - Cancellable - every monitor and backend query accepts a
tokio_util::sync::CancellationToken.tokio::select!cancellation arms are biased so an already-cancelled token always wins the race. - Stream API - terminal methods return
tokio_stream::Stream<Item = Result<PrinterChanges>>/Stream<Item = Result<PropertyChange>>. A terminal backend failure (e.g. sustained WMI/CUPS outage) propagates as the stream's final item before it closes, so callers can distinguish graceful shutdown from a crash. - Print job tracking -
list_jobsreturns typedJob/JobStatusvalues fromWin32_PrintJobon Windows,lpstat -l -oon Linux, or libcups2 FFI when the optionallinux-libcupsfeature is on. - Rich Linux state - CUPS
printer-state-reasonsparsed into the sameErrorState/PrinterStatesurface used on Windows (media-empty, toner-low, cover-open, jammed, etc.). - Typed task panics -
monitor_multiple_printerssurfaces per-printer task panics asPrinterError::TaskPanicked { printer_name, panic_message }; no string parsing needed. - Optional
serde/tracing- off by default; opt in via cargo features. - Library + CLI - use as a crate or as the
printer_monitorbinary.
Quick Start
As a Library
Add this to your Cargo.toml:
[]
= "2.0.0"
= { = "1.0", = ["full"] }
Basic Listing
use ;
async
Find a Specific Printer
use PrinterMonitor;
async
Monitor With the Fluent Builder
PrinterMonitor::monitor(name) returns a MonitorBuilder you can configure with chainable methods, then terminate with run_changes / run_printer / run_property:
use ;
use CancellationToken;
const INTERVAL_MS: u64 = 30_000;
async
Monitor a Single Property
use ;
const INTERVAL_MS: u64 = 60_000;
async
Stream-Based Monitoring
run_changes_stream and run_property_stream return tokio_stream::Stream so you can pipe events through combinators instead of using a callback. Items are Result<T>: an Err is emitted once when the underlying monitor exits because of sustained backend failure, then the stream closes. A clean shutdown (cancellation, receiver drop) closes the stream without emitting an error.
use PrinterMonitor;
use StreamExt;
const INTERVAL_MS: u64 = 1_000;
async
Event-Driven Monitoring (opt-in)
With the events cargo feature enabled, the builder can subscribe to platform event notifications instead of polling: WMI __InstanceModificationEvent on Windows, org.cups.cupsd.Notifier D-Bus signals on Linux. State changes propagate within ~1 second of the platform notification. Without the feature, with_events(true) is accepted silently and the builder falls back to polling.
[]
= { = "2.0.0", = ["events"] }
use PrinterMonitor;
use StreamExt;
async
Monitoring Multiple Printers
use PrinterMonitor;
const INTERVAL_MS: u64 = 30_000;
async
If a per-printer task panics, the call returns PrinterError::TaskPanicked { printer_name, panic_message } so you can match on the failing printer's name without parsing strings.
Print Job Tracking
use PrinterMonitor;
async
On Linux the parser reads lpstat -l -o and maps the Status: line plus IPP job-state-reasons into JobStatus (Printing, Spooling, Paused, Complete, Deleted, Error, ...). With the optional linux-libcups cargo feature, the same call goes through libcups2 via cupsGetJobs2() instead of forking a subprocess - faster, and surfaces structured fields like job title and owner directly.
Cargo Features
| Feature | Default | Effect |
|---|---|---|
rt-multi-thread |
on | Pulls in tokio's multi-threaded runtime. Disable for library-only consumers that bring their own runtime. |
serde |
off | Adds Serialize / Deserialize derives to the public domain types (Printer, Job, status enums, change types). |
tracing |
off | Routes library log calls through the tracing crate instead of log. |
events |
off | Event-driven monitoring: WMI __InstanceModificationEvent on Windows, CUPS D-Bus signals (org.cups.cupsd.Notifier) on Linux. Enables MonitorBuilder::with_events. |
linux-libcups |
off | Linux only. Replaces the lpstat subprocess parser with a libcups2 FFI backend (cupsGetDests2 / cupsGetJobs2). Requires libcups2-dev / cups-devel at build time. |
Example:
= { = "2.0.0", = false, = ["serde", "tracing"] }
= { = "1.0", = ["macros", "rt"] }
Cancellation
Every monitor and the cancellable backend methods accept Option<CancellationToken>. Cancellation is checked both before each poll and inside the sleep tokio::select!, so it stays responsive mid-interval.
use PrinterMonitor;
use CancellationToken;
const INTERVAL_MS: u64 = 5_000;
async
list_printers_cancellable / find_printer_cancellable return PrinterError::Cancelled when the token wins the race.
Migrating From 1.x
2.0 is a breaking release. Highlights:
- Removed:
PrinterMonitor::list_printersandPrinterMonitor::find_printer. Uselist_printers_cancellable(None)andfind_printer_cancellable(name, None)- passSome(token)to abort the query. - Stream item type changed:
run_changes_stream/run_property_streamnow yieldResult<T>instead ofT. A terminalErrpropagates a sustained backend failure before the stream closes; clean shutdowns close the stream silently. PrinterBackend::list_jobsis now required. Downstream backend implementations can no longer silently fall back to an empty-vec default.- All public enums are
#[non_exhaustive](PrinterError,PrinterState,ErrorState,PrinterStatus,JobStatus,MonitorableProperty,PropertyChange). Exhaustivematches need a wildcard arm; future variant additions are non-breaking within 2.x. - New typed error variants:
PrinterError::Cancelled(returned by the*_cancellablemethods when the token wins the race) andPrinterError::TaskPanicked { printer_name, panic_message }(surfaced bymonitor_multiple_printersso callers can match on the failing printer instead of parsing strings).
The positional monitor_printer / monitor_printer_changes / monitor_property methods still exist and are not deprecated; MonitorBuilder is a convenience layer on top of them.
CLI Usage
The crate ships a printer_monitor binary:
# Install from crates.io
# Or run from source
List All Printers
Sample output:
Printer Status Checker
======================
Found 3 printer(s):
Printer #1: HP LaserJet Pro MFP M428f
Status: Idle
Error State: No Error
Offline: No
Default Printer: Yes
Printer #2: HPDC7777 (HP Smart Tank 580-590 series)
Status: Offline
Error State: Service Requested
Offline: Yes
Printer #3: Microsoft Print to PDF
Status: Idle
Error State: No Error
Offline: No
Monitor a Specific Printer
Sample output:
Printer Status Monitor Service
==============================
Monitoring printer 'HP LaserJet Pro' every 60 seconds...
Press Ctrl+C to stop
[2026-05-17 14:30:15] Printer 'HP LaserJet Pro' Initial Status:
Status: Idle
Error State: No Error
Offline: No
[2026-05-17 14:31:15] Checking printer 'HP LaserJet Pro'
[2026-05-17 14:32:15] Printer 'HP LaserJet Pro' Status Changed:
Status: Idle -> Printing
Error State: No Error -> No Error
Offline: No
Platform Support
| Platform | Backend | Requirements | Coverage |
|---|---|---|---|
| Windows | WMI (Win32_Printer + Win32_PrintJob) | None (built-in) | Full .NET PrintQueueStatus flag support and 12 DetectedErrorState values (0-11). Optional event-driven monitoring via __InstanceModificationEvent (cargo feature events). Print jobs via WMI. |
| Linux | CUPS (lpstat) |
cups-client package recommended |
Status from lpstat -l -p, including IPP printer-state-reasons mapped to typed ErrorState / PrinterState. Print jobs via lpstat -l -o. Subprocess calls run under LANG=C with a 5-second timeout. |
Linux Setup
Ubuntu/Debian:
# Add the libcups development headers if you plan to build with --features linux-libcups
RHEL/CentOS/Fedora:
# For --features linux-libcups
API Reference
Core Types
PrinterMonitor- main entry point. CheaplyCloneable (Arc<dyn PrinterBackend>inside) so multiple tasks can share one backend connection.MonitorBuilder- fluent configuration for per-printer monitoring runs.Printer- represents a printer plus all platform-specific raw codes.Job/JobStatus- typed print-job snapshot returned bylist_jobs.MonitorableProperty- type-safe enum naming each monitorable property.PrinterStatus- operational status enum (values 1-7).PrinterState- .NET PrintQueueStatus flags (PaperJam,TonerLow,DoorOpen, ...).ErrorState- DetectedErrorState enum (NoError,Jammed,NoPaper, ...).PrinterChanges/PropertyChange- diff types emitted by change monitors.PrinterError- error enum (WmiError,CupsError,PrinterNotFound,PlatformNotSupported,IoError,Cancelled,TaskPanicked { printer_name, panic_message },Other).CancellationToken- re-exported fromtokio_util::syncfor convenience.
MonitorBuilder Methods
| Method | Effect |
|---|---|
interval_ms(ms) |
Polling interval. Default: 60 000 ms. |
cancel_token(token) |
Attach a CancellationToken. |
wait_for_appearance(bool) |
When false, return PrinterError::PrinterNotFound on the first poll if the printer is missing. Default true (wait silently). |
filter_property(prop) |
Required by run_property / run_property_stream. Filters change events to a single property. |
with_events(bool) |
Use WMI event subscription when on Windows with events feature; falls back to polling otherwise. |
run_changes(callback) |
Callback receives a PrinterChanges per poll that detected mutations. |
run_printer(callback) |
Callback receives (current, previous) snapshots. |
run_property(callback) |
Callback receives a single PropertyChange matching filter_property. |
run_changes_stream() |
Returns Stream<Item = Result<PrinterChanges>>. Terminal backend failure is emitted as the stream's final Err item. |
run_property_stream() |
Returns Result<Stream<Item = Result<PropertyChange>>>. Outer Result errors if filter_property was not set; inner mirrors the changes-stream contract. |
Available Properties to Monitor
Polling Intervals
All monitoring functions take an interval in milliseconds. Common values:
100- 0.1 s, high frequency500- 0.5 s, responsive1000- 1 s, standard5000- 5 s, moderate30000- 30 s, conservative60000- 1 minute, low frequency
Raw WMI Property Access (Windows)
Printer preserves the raw WMI codes alongside the typed enums:
printer.printer_status_code // Option<u32> - PrinterStatus (1-7)
printer.printer_state_code // Option<u32> - PrinterState (.NET PrintQueueStatus flags)
printer.detected_error_state_code // Option<u32> - DetectedErrorState (0-11)
printer.extended_printer_status_code // Option<u32> - ExtendedPrinterStatus
printer.extended_detected_error_state_code // Option<u32> - ExtendedDetectedErrorState
printer.wmi_status // Option<&str> - Status property
Each one has a matching *_description() helper that returns a human-readable &'static str.
WMI Status Values
wmi_status() mirrors Microsoft's documented Status values:
"OK"- normal functioning"Degraded"- functioning with issues"Error"- has problems"Unknown"- cannot determine status"No Contact"- communication lost
Example: Detailed Analysis
let printer = monitor
.find_printer_cancellable
.await?
.expect;
println!;
println!;
println!;
println!;
if let Some = printer.printer_status_code
if let Some = printer.extended_printer_status_code
if let Some = printer.wmi_status
Status Enums
PrinterStatus (Current Property, Values 1-7)
PrinterState (.NET PrintQueueStatus Flags)
Based on .NET System.Printing.PrintQueueStatus. The Linux backend now feeds the same enum via IPP printer-state-reasons.
Note: PrinterState values are bitwise flags, so multiple states can be active simultaneously. The library picks the most informative single variant via a priority chain (specific causes such as PaperJam or DoorOpen win over the generic Error bit).
ErrorState (Win32_Printer DetectedErrorState Values)
Examples
The examples directory holds runnable usage patterns. Examples have their own Cargo.toml to keep the main library lightweight. See examples/README.md for a recommended reading order.
basic_listing.rs- list all printers with detailed information.monitor_changes.rs- monitor status changes over time.property_monitoring.rs- property-level change detection.streaming_changes.rs-run_changes_stream/run_property_streamwithStreamExtcombinators.events_demo.rs-with_events(true)for WMI / D-Bus event subscriptions.jobs_listing.rs-list_jobsacross all printers or a single queue.error_handling.rs- graceful error handling, includingCancelled/TaskPanickedmatching.async_patterns.rs- concurrent monitoring patterns.cancellation_token_example.rs- graceful shutdown viaCancellationToken.
Run from the repo root:
Contributing
Contributions are welcome. For major changes, please open an issue first to discuss what you would like to change.
Development
# Feature combinations
License
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Changelog
See CHANGELOG.md for details about changes in each version.