netscape-cookie-file-parser 0.3.0

Parse Netscape/curl cookie jar files while preserving raw cookie bytes.
Documentation
use std::error::Error;
use std::fmt;

/// Error returned when parsing a cookie stream.
///
/// Single-line parsing returns [`ParseErrorKind`] directly. Stream parsing wraps
/// the kind with the 1-based line number where parsing failed.
#[derive(Debug)]
pub struct ParseError {
    /// 1-based line number in the input stream.
    pub line: usize,
    /// Detailed parse failure.
    pub kind: ParseErrorKind,
}

/// Detailed reason a cookie line or stream could not be parsed.
#[derive(Debug)]
pub enum ParseErrorKind {
    /// The underlying buffered reader failed.
    Io(std::io::Error),
    /// A non-comment cookie line had a field count other than seven.
    MissingFields { found: usize },
    /// The expires field was empty, non-decimal, or overflowed `u64`.
    InvalidExpires,
    /// The cookie name or value contained an ASCII control octet.
    InvalidOctets,
}

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

impl fmt::Display for ParseErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(error) => write!(f, "read error: {error}"),
            Self::MissingFields { found } => {
                write!(f, "expected 7 tab-separated fields, found {found}")
            }
            Self::InvalidExpires => f.write_str("invalid expires timestamp"),
            Self::InvalidOctets => f.write_str("cookie name or value contains control octets"),
        }
    }
}

impl Error for ParseError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.kind {
            ParseErrorKind::Io(error) => Some(error),
            _ => None,
        }
    }
}