# cups_rs
A safe Rust wrapper for the Common UNIX Printing System (CUPS) API.
[](https://crates.io/crates/cups_rs)
[](https://docs.rs/cups_rs)
## Features
- **Safe CUPS Integration**: Memory-safe Rust bindings to the CUPS printing system
- **Printer Discovery**: Enumerate and filter available printers with callbacks
- **Comprehensive Printer Information**: Access printer capabilities, media support, and status
- **Job Management**: Create, submit, monitor, and cancel print jobs
- **Print Options**: Type-safe configuration for copies, quality, color mode, duplex, and media
- **Media Handling**: Query supported paper sizes with detailed margin information
- **Error Handling**: Comprehensive error types with recovery suggestions
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
cups_rs = "0.1.0"
```
### System Requirements
CUPS development libraries must be installed:
**Ubuntu/Debian:**
```bash
sudo apt-get install libcups2-dev
```
**RHEL/CentOS/Fedora:**
```bash
sudo dnf install cups-devel
# or: sudo yum install cups-devel
```
**macOS:**
```bash
# CUPS is included with macOS
```
## Usage
### Discovering Printers
```rust
use cups_rs::*;
// Get all available printers
let printers = get_all_destinations()?;
println!("Found {} printer(s)", printers.len());
// Get default printer
let default = get_default_destination()?;
println!("Default: {}", default.full_name());
// Get specific printer by name
let printer = get_destination("PDF")?;
```
### Printer Information and Status
```rust
// Check printer state and capabilities
println!("State: {}", printer.state());
println!("Accepting jobs: {}", printer.is_accepting_jobs());
if let Some(info) = printer.info() {
println!("Description: {}", info);
}
if let Some(location) = printer.location() {
println!("Location: {}", location);
}
// Check for issues
let reasons = printer.state_reasons();
if !reasons.is_empty() {
println!("Issues: {}", reasons.join(", "));
}
```
### Printer Capabilities and Media
```rust
use std::ptr;
// Get detailed printer capabilities
let info = printer.get_detailed_info(ptr::null_mut())?;
// Check option support
let supports_duplex = printer.is_option_supported(ptr::null_mut(), SIDES);
let supports_color = printer.is_option_supported(ptr::null_mut(), PRINT_COLOR_MODE);
// Check media support
let supports_a4 = info.is_value_supported(
ptr::null_mut(),
printer.as_ptr(),
MEDIA,
MEDIA_A4
);
// Get available media sizes
let media_sizes = info.get_all_media(
ptr::null_mut(),
printer.as_ptr(),
MEDIA_FLAGS_DEFAULT
)?;
for media in &media_sizes {
println!("{}: {:.1}\" × {:.1}\"",
media.name,
media.width_inches(),
media.length_inches()
);
}
```
### Creating and Managing Print Jobs
```rust
// Create a simple print job
let job = create_job(&printer, "My Document")?;
println!("Created job ID: {}", job.id);
// Submit a document
job.submit_file("document.pdf", FORMAT_PDF)?;
// Close job to start printing
job.close()?;
```
### Print Options and Configuration
```rust
// Create job with specific options
let options = PrintOptions::new()
.copies(2)
.color_mode(ColorMode::Color)
.quality(PrintQuality::High)
.duplex(DuplexMode::TwoSidedPortrait)
.media(MEDIA_A4)
.orientation(Orientation::Landscape);
let job = create_job_with_options(&printer, "Configured Print", &options)?;
// Submit document with custom format
job.submit_file("presentation.pdf", FORMAT_PDF)?;
job.close()?;
```
### Job Monitoring and Management
```rust
// Get job information
let job_info = get_job_info(job.id)?;
// List active jobs
let active_jobs = get_active_jobs(None)?;
for job in &active_jobs {
println!("Job {}: {} ({})", job.id, job.title, job.status);
}
// Cancel a specific job
cancel_job(job.id)?;
// Or cancel via job instance
job.cancel()?;
```
### Advanced Printer Discovery
```rust
// Find printers with specific capabilities
let color_printers = find_destinations(PRINTER_COLOR, PRINTER_BW)?;
let local_printers = find_destinations(PRINTER_LOCAL, PRINTER_REMOTE)?;
// Use callback-based enumeration for real-time updates
enum_destinations(
DEST_FLAGS_NONE,
5000, // 5 second timeout
None,
PRINTER_LOCAL,
PRINTER_REMOTE,
&mut |flags, dest, user_data: &mut Vec<String>| {
if (flags & DEST_FLAGS_REMOVED) == 0 {
user_data.push(dest.full_name());
println!("Found: {}", dest.full_name());
}
true // Continue enumeration
},
&mut printer_names,
)?;
```
### Error Handling
```rust
use cups_rs::{Error, ErrorCategory};
match create_job(&printer, "Test") {
Ok(job) => println!("Job created: {}", job.id),
Err(e) => {
println!("Error: {}", e);
println!("Category: {:?}", e.error_category());
println!("Suggestion: {}", e.suggested_action());
if e.is_recoverable() {
println!("This error may be temporary - consider retrying");
}
}
}
```
### Media Size Details
```rust
// Get default media with detailed information
let default_media = info.get_default_media(
ptr::null_mut(),
printer.as_ptr(),
MEDIA_FLAGS_DEFAULT
)?;
println!("Default media: {}", default_media.name);
println!("Size: {:.1}\" × {:.1}\"",
default_media.width_inches(),
default_media.length_inches()
);
println!("Printable area: {:.1}\" × {:.1}\"",
default_media.printable_width_inches(),
default_media.printable_length_inches()
);
println!("Margins: T:{:.1}\" B:{:.1}\" L:{:.1}\" R:{:.1}\"",
default_media.top_margin_inches(),
default_media.bottom_margin_inches(),
default_media.left_margin_inches(),
default_media.right_margin_inches()
);
```
## Examples
The [examples](examples/) directory contains complete working examples:
- [`discover_printers.rs`](examples/discover_printers.rs): Basic printer discovery and information
- [`printer_capabilities.rs`](examples/printer_capabilities.rs): Exploring printer features and media support
- [`print_with_options.rs`](examples/print_with_options.rs): Advanced printing with various options
- [`complete_workflow.rs`](examples/complete_workflow.rs): Full job lifecycle management
Run examples with:
```bash
cargo run --example discover_printers
cargo run --example printer_capabilities -- PDF
cargo run --example complete_workflow -- document.pdf MyPrinter
```
## Supported Print Options
| `copies` | `u32` | Number of copies |
| `media` | `&str` | `MEDIA_A4`, `MEDIA_LETTER`, `MEDIA_LEGAL`, etc. |
| `color_mode` | `ColorMode` | `Auto`, `Color`, `Monochrome` |
| `quality` | `PrintQuality` | `Draft`, `Normal`, `High` |
| `duplex` | `DuplexMode` | `OneSided`, `TwoSidedPortrait`, `TwoSidedLandscape` |
| `orientation` | `Orientation` | `Portrait`, `Landscape` |
## Supported Document Formats
- **PDF**: `FORMAT_PDF` (`application/pdf`)
- **PostScript**: `FORMAT_POSTSCRIPT` (`application/postscript`)
- **Plain Text**: `FORMAT_TEXT` (`text/plain`)
- **JPEG Images**: `FORMAT_JPEG` (`image/jpeg`)
## Error Types
The library provides detailed error information:
- `DestinationNotFound`: Printer not available
- `JobCreationFailed`: Cannot create print job
- `PrinterNotAccepting`: Printer rejecting jobs
- `AuthenticationRequired`: Credentials needed
- `DocumentTooLarge`: File size limits exceeded
- `NetworkError`: CUPS server communication issues
## Thread Safety
CUPS operations are not thread-safe by default. For multi-threaded applications, consider:
- Using a single thread for all CUPS operations
- Implementing proper synchronization around CUPS calls
- Creating separate connections per thread where possible
## License
MIT License
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Requirements
- Rust 1.70+
- CUPS 2.0+ development libraries
- Linux, macOS, or other UNIX-like system with CUPS support