A safe Rust wrapper for the Common UNIX Printing System (CUPS) API.
- 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
 
Add this to your Cargo.toml:
[dependencies]
cups_rs = "0.1.0"CUPS development libraries must be installed:
Ubuntu/Debian:
sudo apt-get install libcups2-devRHEL/CentOS/Fedora:
sudo dnf install cups-devel
# or: sudo yum install cups-develmacOS:
# CUPS is included with macOSuse 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")?;// 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(", "));
}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()
    );
}// 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()?;// 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()?;// Get job information
let job_info = get_job_info(job.id)?;
println!("Status: {} | Size: {} bytes", job_info.status, job_info.size);
// 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()?;// 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,
)?;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");
        }
    }
}// 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()
);The examples directory contains complete working examples:
discover_printers.rs: Basic printer discovery and informationprinter_capabilities.rs: Exploring printer features and media supportprint_with_options.rs: Advanced printing with various optionscomplete_workflow.rs: Full job lifecycle management
Run examples with:
cargo run --example discover_printers
cargo run --example printer_capabilities -- PDF
cargo run --example complete_workflow -- document.pdf MyPrinter| Option | Type | Values | 
|---|---|---|
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 | 
- PDF: 
FORMAT_PDF(application/pdf) - PostScript: 
FORMAT_POSTSCRIPT(application/postscript) - Plain Text: 
FORMAT_TEXT(text/plain) - JPEG Images: 
FORMAT_JPEG(image/jpeg) 
The library provides detailed error information:
DestinationNotFound: Printer not availableJobCreationFailed: Cannot create print jobPrinterNotAccepting: Printer rejecting jobsAuthenticationRequired: Credentials neededDocumentTooLarge: File size limits exceededNetworkError: CUPS server communication issues
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
 
MIT License
Contributions are welcome! Please feel free to submit a Pull Request.
- Rust 1.70+
 - CUPS 2.0+ development libraries
 - Linux, macOS, or other UNIX-like system with CUPS support