zpl_toolchain_print_client 0.1.8

Print client for sending ZPL to Zebra and ZPL-compatible printers over TCP, USB, and serial
Documentation
//! Minimal print job lifecycle semantics (F13).
//!
//! Provides job IDs and phase vocabulary for batch printing and completion tracking.
//! See `contracts/fixtures/print-job-lifecycle.v1.json` for the shared schema.

use std::sync::atomic::{AtomicU64, Ordering};

/// Opaque identifier for a logical print job (single label or batch).
///
/// Generated by [`create_job_id()`]. Use for correlation across batch send and
/// completion wait operations.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct JobId(String);

impl JobId {
    /// Create a new job ID from an opaque string (e.g. UUID from external source).
    pub fn from_string(s: impl Into<String>) -> Self {
        Self(s.into())
    }

    /// Return the underlying string.
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Display for JobId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

static JOB_COUNTER: AtomicU64 = AtomicU64::new(0);

/// Generate a new unique job ID.
///
/// Format: `job-{nanos}-{counter}` for process-local uniqueness without
/// external dependencies.
pub fn create_job_id() -> JobId {
    let now = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_nanos();
    let c = JOB_COUNTER.fetch_add(1, Ordering::Relaxed);
    JobId::from_string(format!("job-{}-{}", now, c))
}

/// Lifecycle phase of a print job.
///
/// Aligned with `contracts/fixtures/print-job-lifecycle.v1.json`.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum JobPhase {
    /// Job created, not yet transmitted.
    Queued,

    /// Bytes being transmitted to printer.
    Sending,

    /// All bytes delivered to printer. Deterministic when send returns `Ok`.
    Sent,

    /// Printer has data; physical printing in progress.
    Printing,

    /// Printer reports idle (formats_in_buffer=0, labels_remaining=0).
    /// Deterministic when `wait_for_completion` returns `Ok`.
    Completed,

    /// Error occurred during send or completion.
    Failed,

    /// User cancelled (e.g. ControlFlow::Break).
    Aborted,
}

impl JobPhase {
    /// Returns `true` if this phase indicates a terminal state.
    pub fn is_terminal(&self) -> bool {
        matches!(
            self,
            JobPhase::Completed | JobPhase::Failed | JobPhase::Aborted
        )
    }
}