tastty-core 0.1.0

Sans-IO core of the tastty terminal session library: VT parser, screen buffer, and byte encoders.
/// Decode a hex-encoded ASCII string (e.g. "544e" → "TN").
fn hex_decode(hex: &str) -> Option<String> {
    let bytes = hex.as_bytes();
    if !bytes.len().is_multiple_of(2) {
        return None;
    }
    let mut out = Vec::with_capacity(bytes.len() / 2);
    for chunk in bytes.chunks(2) {
        let hi = hex_nibble(chunk[0])?;
        let lo = hex_nibble(chunk[1])?;
        out.push((hi << 4) | lo);
    }
    String::from_utf8(out).ok()
}

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

/// Look up a hex-encoded terminfo capability name.
/// Returns `Some(value_bytes)` if known, `None` if not recognized.
/// TN is excluded here; it comes from the host profile.
pub(crate) fn xtgettcap_lookup(hex_key: &str) -> Option<&'static [u8]> {
    let name = hex_decode(hex_key)?;
    match name.as_str() {
        "RGB" => Some(b"8"),
        "Tc" => Some(b""),
        "Smulx" => Some(b"\\E[4:%p1%dm"),
        "Ss" => Some(b"\\E[%p1%d q"),
        "Se" => Some(b"\x1b[2 q"),
        "hpa" => Some(b"\\E[%i%p1%dG"),
        "sitm" => Some(b"\x1b[3m"),
        "ritm" => Some(b"\x1b[23m"),
        "Ms" => Some(b"\\E]52;%p1%s;%p2%s\\007"),
        _ => None,
    }
}

/// Decode a hex key and check if it's "TN" (terminal name).
pub(crate) fn xtgettcap_is_tn(hex_key: &str) -> bool {
    hex_decode(hex_key).as_deref() == Some("TN")
}