1use color_core::RGBA8;
2use pex::{
3 helpers::{ascii_whitespace, make_from_str},
4 ParseResult, ParseState, StopBecause,
5};
6
7pub fn hex(input: &str) -> Result<RGBA8, StopBecause> {
8 let state = ParseState::new(input.trim_end()).skip(ascii_whitespace);
9 make_from_str(state, parse_hex)
10}
11
12pub fn parse_hex(input: ParseState) -> ParseResult<RGBA8> {
15 let (state, _) = input.match_char('#')?;
16 let (state, hex) = state.match_str_if(|c| c.is_ascii_hexdigit(), "ASCII_HEX")?;
17 let rgba = match hex.as_bytes() {
18 [gray] => RGBA8::gray(byte2_to_u8(*gray, *gray)),
19 [gray1, gray2] => RGBA8::gray(byte2_to_u8(*gray1, *gray2)),
20 [r, g, b] => RGBA8::new(byte2_to_u8(*r, *r), byte2_to_u8(*g, *g), byte2_to_u8(*b, *b), 255),
21 [r, g, b, a] => RGBA8::new(byte2_to_u8(*r, *r), byte2_to_u8(*g, *g), byte2_to_u8(*b, *b), byte2_to_u8(*a, *a)),
22 [r1, r2, g1, g2, b1, b2] => RGBA8::new(byte2_to_u8(*r1, *r2), byte2_to_u8(*g1, *g2), byte2_to_u8(*b1, *b2), 255),
23 [r1, r2, g1, g2, b1, b2, a1, a2] =>
24 RGBA8::new(byte2_to_u8(*r1, *r2), byte2_to_u8(*g1, *g2), byte2_to_u8(*b1, *b2), byte2_to_u8(*a1, *a2)),
25 _ => StopBecause::must_be("3, 4, 6 or 8 hex digits", state.start_offset)?,
26 };
27 state.finish(rgba)
28}
29
30#[inline(always)]
31fn byte2_to_u8(high: u8, low: u8) -> u8 {
32 byte_to_u8(high) << 4 | byte_to_u8(low)
33}
34
35#[inline(always)]
36fn byte_to_u8(byte: u8) -> u8 {
37 match byte {
38 b'0'..=b'9' => byte - b'0',
39 b'a'..=b'f' => byte - b'a' + 10,
40 b'A'..=b'F' => byte - b'A' + 10,
41 _ => unreachable!(),
42 }
43}