luff 0.2.1

Print files with formatting
Documentation
//! Clipboard-specific error types
//!
//! Structured errors for clipboard operations, allowing callers to
//! distinguish transient failures (e.g. timeout) from permanent ones
//! (e.g. size exceeded) and react accordingly.

/// Errors that can occur during clipboard operations.
#[derive(Debug, thiserror::Error)]
pub enum ClipboardError {
    /// Content exceeds the maximum clipboard size limit.
    #[error(
        "Content too large for clipboard: {content_len} bytes exceeds {limit} byte limit. \
         Consider using streaming mode (without --clip) or processing fewer files."
    )]
    SizeExceeded {
        /// Actual content length in bytes
        content_len: usize,
        /// Configured limit in bytes
        limit: usize,
    },

    /// Failed to access the system clipboard (e.g. no display server).
    #[error("Failed to access clipboard: {0}")]
    Access(String),

    /// Failed to write content to the clipboard.
    #[error("Failed to copy to clipboard: {0}")]
    Write(String),

    /// Failed to spawn the clipboard writer thread.
    #[error("Failed to spawn clipboard thread: {0}")]
    ThreadSpawn(#[from] std::io::Error),

    /// Clipboard operation exceeded the timeout.
    #[error("Clipboard operation timed out (system clipboard might be locked)")]
    Timeout,

    /// Clipboard thread exited without producing a result.
    #[error("Clipboard thread exited without sending a result")]
    ThreadLost,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_size_exceeded_display_includes_both_values() {
        let err = ClipboardError::SizeExceeded {
            content_len: 2_000_000,
            limit: 1_000_000,
        };
        let msg = err.to_string();
        assert!(
            msg.contains("2000000") && msg.contains("1000000"),
            "Display should include both content_len and limit: {msg}"
        );
    }

    #[test]
    fn test_access_error_display() {
        let err = ClipboardError::Access("no display server".to_string());
        assert!(err.to_string().contains("no display server"));
    }

    #[test]
    fn test_write_error_display() {
        let err = ClipboardError::Write("permission denied".to_string());
        assert!(err.to_string().contains("permission denied"));
    }

    #[test]
    fn test_timeout_error_display() {
        let err = ClipboardError::Timeout;
        assert!(err.to_string().contains("timed out"));
    }

    #[test]
    fn test_thread_lost_error_display() {
        let err = ClipboardError::ThreadLost;
        assert!(err.to_string().contains("without sending a result"));
    }

    #[test]
    fn test_thread_spawn_from_io_error() {
        let io_err = std::io::Error::other("no threads");
        let err = ClipboardError::from(io_err);
        assert!(matches!(err, ClipboardError::ThreadSpawn(_)));
    }
}