/// Compile-time validation and creation of Pyth feed IDs from hex strings.
/// Accepts either 64-character hex or 66-character hex with "0x" prefix.
#[macro_export]
macro_rules! feed_id {
    ($hex:expr) => {{
        // These const fns only run at compile time; panics become compiler errors.
        #[allow(clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::panic, clippy::arithmetic_side_effects, reason = "compile-time const fn")]
        const fn hex_char_to_byte(c: u8) -> u8 {
            match c {
                b'0'..=b'9' => c - b'0',
                b'a'..=b'f' => c - b'a' + 10,
                b'A'..=b'F' => c - b'A' + 10,
                _ => panic!("Invalid hex character"),
            }
        }

        #[allow(clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::panic, clippy::arithmetic_side_effects, reason = "compile-time const fn")]
        const fn parse_feed_id(hex: &str) -> PythFeedId {
            let hex_bytes = hex.as_bytes();
            let (start_offset, expected_len) =
                if hex_bytes.len() == 66 && hex_bytes[0] == b'0' && hex_bytes[1] == b'x' {
                    (2, 66)
                } else if hex_bytes.len() == 64 {
                    (0, 64)
                } else {
                    panic!("Feed ID must be 64 hex characters or 66 with 0x prefix")
                };

            if hex_bytes.len() != expected_len {
                panic!("Invalid hex string length");
            }

            let mut result = [0u8; 32];
            let mut i = 0;
            while i < 32 {
                let high = hex_char_to_byte(hex_bytes[start_offset + i * 2]);
                let low = hex_char_to_byte(hex_bytes[start_offset + i * 2 + 1]);
                result[i] = (high << 4) | low;
                i += 1;
            }
            result
        }

        const HEX: &str = $hex;
        const RESULT: PythFeedId = parse_feed_id(HEX);
        RESULT
    }};
}

/// Compile-time filtering of supported tokens by a given field.
#[macro_export]
macro_rules! filter_supported_tokens {
    ($vis:vis $name:ident, $field:ident) => {
        #[allow(clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::arithmetic_side_effects, reason = "compile-time const evaluation")]
        $vis const $name: [Token; {
            let mut c = 0usize;
            let mut i = 0usize;
            while i < SUPPORTED_TOKENS.len() {
                if SUPPORTED_TOKENS[i].$field.is_some() { c += 1; }
                i += 1;
            }
            c
        }] = {
            const N: usize = {
                let mut c = 0usize;
                let mut i = 0usize;
                while i < SUPPORTED_TOKENS.len() {
                    if SUPPORTED_TOKENS[i].$field.is_some() { c += 1; }
                    i += 1;
                }
                c
            };
            let mut out = [SUPPORTED_TOKENS[0]; N];
            let mut j = 0usize;
            let mut i = 0usize;
            while i < SUPPORTED_TOKENS.len() {
                if SUPPORTED_TOKENS[i].$field.is_some() {
                    out[j] = SUPPORTED_TOKENS[i];
                    j += 1;
                }
                i += 1;
            }
            out
        };
    };
}
