prefix_hex/
lib.rs

1// Copyright 2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! The `prefix-hex` crates offers encoding and decoding of hex strings with a `0x` prefix.
5//!
6//! Its API aims to mimic that of the [`hex`](https://docs.rs/hex/latest/hex/) crate, which we also use internally.
7//!
8//! This crate is compatible with the hex encoding rules of the [Ethereum RPC API](https://eth.wiki/json-rpc/API#hex-value-encoding).
9
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13#[cfg(feature = "std")]
14extern crate std;
15
16mod data;
17mod error;
18#[cfg(feature = "primitive-types")]
19mod primitive_types;
20
21use alloc::string::String;
22
23pub use error::Error;
24
25/// Tries to decode an hexadecimal encoded string with a `0x` prefix.
26pub trait FromHexPrefixed: Sized {
27    /// Tries to decode an hexadecimal encoded string with a `0x` prefix.
28    fn from_hex_prefixed(hex: impl AsRef<str>) -> Result<Self, Error>;
29}
30
31/// Encodes data into an hexadecimal encoded string with a `0x` prefix.
32pub trait ToHexPrefixed {
33    /// Encodes data into an hexadecimal encoded string with a `0x` prefix.
34    fn to_hex_prefixed(self) -> String;
35}
36
37/// Decodes a hex string with `0x` prefix into a type `T`.
38///
39/// ## Decode into `Vec<u8>`
40/// ```
41/// let result = prefix_hex::decode("0x000102");
42/// assert_eq!(result, Ok(vec![0x0, 0x1, 0x2]));
43/// ```
44/// ## Decode into `[u8;N]`
45/// ```
46/// let result = prefix_hex::decode("0x000102");
47/// assert_eq!(result, Ok([0x0, 0x1, 0x2]));
48/// ```
49pub fn decode<T: FromHexPrefixed>(hex: impl AsRef<str>) -> Result<T, Error> {
50    T::from_hex_prefixed(hex)
51}
52
53/// Encodes `T` as a hex string with a `0x` prefix.
54///
55/// ## Encode `Vec<u8>`
56/// ```
57/// assert_eq!(prefix_hex::encode(vec![0x1, 0x2, 0x3]), "0x010203");
58/// ```
59/// ## Encode `[u8; N]`
60/// ```
61/// assert_eq!(prefix_hex::encode([0x1, 0x2, 0x3]), "0x010203");
62/// ```
63pub fn encode<T: ToHexPrefixed>(value: T) -> String {
64    ToHexPrefixed::to_hex_prefixed(value)
65}
66
67// TODO: Maybe introduce `handle_error` function with `#[cold]` attribute.
68fn strip_prefix(hex: &str) -> Result<&str, Error> {
69    if let Some(hex) = hex.strip_prefix("0x") {
70        Ok(hex)
71    } else if hex.len() < 2 {
72        Err(Error::InvalidStringLength)
73    } else {
74        let mut chars = hex.chars();
75        // Panic: the following two operations cannot panic because we checked for `hex.len()` in the `else if` branch.
76        let c0 = chars.next().unwrap();
77        let c1 = chars.next().unwrap();
78        Err(Error::InvalidPrefix { c0, c1 })
79    }
80}