sift-query-core 0.1.0

Backend-independent query model and strict parameter parser for SiftQuery
Documentation
use crate::ParamError;

pub(super) fn decode_component(input: &str) -> Result<String, ParamError> {
    let bytes = input.as_bytes();
    let mut output = Vec::with_capacity(bytes.len());
    let mut index = 0usize;
    while index < bytes.len() {
        match bytes[index] {
            b'+' => {
                output.push(b' ');
                index += 1;
            }
            b'%' => {
                if index + 2 >= bytes.len() {
                    return Err(ParamError::InvalidEncoding);
                }
                let high = hex(bytes[index + 1]).ok_or(ParamError::InvalidEncoding)?;
                let low = hex(bytes[index + 2]).ok_or(ParamError::InvalidEncoding)?;
                output.push((high << 4) | low);
                index += 3;
            }
            byte => {
                output.push(byte);
                index += 1;
            }
        }
    }
    String::from_utf8(output).map_err(|_| ParamError::InvalidEncoding)
}

pub(super) fn encoded_len(input: &str) -> usize {
    input
        .bytes()
        .map(|byte| {
            if byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b'~') {
                1
            } else {
                3
            }
        })
        .sum()
}

pub(super) fn encode_component(input: &str, output: &mut String) {
    for byte in input.bytes() {
        if byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b'~') {
            output.push(char::from(byte));
        } else {
            output.push('%');
            output.push(char::from(b"0123456789ABCDEF"[(byte >> 4) as usize]));
            output.push(char::from(b"0123456789ABCDEF"[(byte & 0x0f) as usize]));
        }
    }
}

const fn hex(byte: u8) -> Option<u8> {
    match byte {
        b'0'..=b'9' => Some(byte - b'0'),
        b'a'..=b'f' => Some(byte - b'a' + 10),
        b'A'..=b'F' => Some(byte - b'A' + 10),
        _ => None,
    }
}