cardanowall-cli 0.2.0

The cardanowall CLI: a standalone Label 309 Proof-of-Existence verifier and toolkit.
Documentation
//! Hex helpers for CLI inputs.
//!
//! [`hex_to_bytes`] is the lenient CLI-input decoder: it accepts an optional
//! `0x`/`0X` prefix and either case, and rejects odd-length / non-hex input with
//! a message that echoes the offending value so the user can copy-paste-debug
//! their command line. This is intentionally more forgiving than the SDK's strict
//! lowercase decoder, which is fed machine-generated bytes.
//!
//! [`bytes_to_hex`] re-exports the single SDK encoder so CLI output stays
//! byte-identical to the SDK's hex form (lowercase, no separators).

/// Encode bytes to lowercase hex with no separators (the SDK encoder).
#[must_use]
pub fn bytes_to_hex(bytes: &[u8]) -> String {
    cardanowall::hex::encode(bytes)
}

/// Decode a lenient hex string: an optional `0x`/`0X` prefix, either case.
///
/// # Errors
///
/// Returns an error message string for odd-length or non-hex input; the message
/// echoes the original value.
pub fn hex_to_bytes(input: &str) -> Result<Vec<u8>, String> {
    let clean = input
        .strip_prefix("0x")
        .or_else(|| input.strip_prefix("0X"))
        .unwrap_or(input);
    if !clean.len().is_multiple_of(2) {
        return Err(format!("hex value has odd length: {input}"));
    }
    if !clean.bytes().all(|b| b.is_ascii_hexdigit()) {
        return Err(format!("hex value contains non-hex chars: {input}"));
    }
    hex::decode(clean).map_err(|e| format!("hex value could not be decoded: {input} ({e})"))
}

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

    #[test]
    fn round_trips_lowercase() {
        let bytes = [0xde, 0xad, 0xbe, 0xef];
        assert_eq!(bytes_to_hex(&bytes), "deadbeef");
        assert_eq!(hex_to_bytes("deadbeef").unwrap(), bytes);
    }

    #[test]
    fn accepts_0x_prefix_and_uppercase() {
        assert_eq!(hex_to_bytes("0xDEAD").unwrap(), vec![0xde, 0xad]);
        assert_eq!(hex_to_bytes("0XdeAd").unwrap(), vec![0xde, 0xad]);
    }

    #[test]
    fn rejects_odd_length() {
        assert!(hex_to_bytes("abc").unwrap_err().contains("odd length"));
    }

    #[test]
    fn rejects_non_hex() {
        assert!(hex_to_bytes("zzzz").unwrap_err().contains("non-hex"));
    }
}