use crate::stream::error::ParseError;
#[cfg(feature = "alloc")]
#[allow(
unused_imports,
reason = "alloc prelude items; subset used per cfg/feature combination"
)]
use alloc::format;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tritet {
AnB64 = 0,
CtB64 = 1,
OpB64 = 2,
Json = 3,
Mgpk1 = 4,
Cbor = 5,
Mgpk2 = 6,
CtOpB2 = 7,
}
#[must_use]
pub fn detect_tritet(byte: u8) -> Tritet {
match byte >> 5 {
0 => Tritet::AnB64,
1 => Tritet::CtB64,
2 => Tritet::OpB64,
3 => Tritet::Json,
4 => Tritet::Mgpk1,
5 => Tritet::Cbor,
6 => Tritet::Mgpk2,
7 => Tritet::CtOpB2,
_ => unreachable!(),
}
}
impl From<Tritet> for ColdCode {
fn from(t: Tritet) -> Self {
match t {
Tritet::AnB64 | Tritet::CtB64 | Tritet::OpB64 => Self::CesrBase64,
Tritet::Json => Self::Json,
Tritet::Mgpk1 | Tritet::Mgpk2 => Self::MessagePack,
Tritet::Cbor => Self::Cbor,
Tritet::CtOpB2 => Self::CesrBinary,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColdCode {
CesrBase64,
CesrBinary,
Json,
Cbor,
MessagePack,
}
pub(crate) fn detect_cold_code(first_byte: u8) -> Result<ColdCode, ParseError> {
match first_byte {
b'{' => Ok(ColdCode::Json),
0xa0..=0xbf => Ok(ColdCode::Cbor),
0x80..=0x8f | 0xde | 0xdf => Ok(ColdCode::MessagePack),
b if b & 0x80 != 0 => Ok(ColdCode::CesrBinary),
b if b.is_ascii_alphanumeric() || b == b'-' || b == b'_' => Ok(ColdCode::CesrBase64),
_ => Err(ParseError::Malformed(format!(
"unrecognized stream byte: 0x{first_byte:02x}"
))),
}
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::as_conversions,
reason = "test code: panics and type conversions acceptable"
)]
mod tests {
use super::*;
#[test]
fn detect_json() {
assert_eq!(detect_cold_code(b'{'), Ok(ColdCode::Json));
}
#[test]
fn detect_cesr_base64_letters() {
assert_eq!(detect_cold_code(b'A'), Ok(ColdCode::CesrBase64));
assert_eq!(detect_cold_code(b'z'), Ok(ColdCode::CesrBase64));
assert_eq!(detect_cold_code(b'-'), Ok(ColdCode::CesrBase64));
assert_eq!(detect_cold_code(b'_'), Ok(ColdCode::CesrBase64));
}
#[test]
fn detect_cesr_base64_digits() {
assert_eq!(detect_cold_code(b'0'), Ok(ColdCode::CesrBase64));
assert_eq!(detect_cold_code(b'9'), Ok(ColdCode::CesrBase64));
}
#[test]
fn detect_cbor() {
assert_eq!(detect_cold_code(0xa0), Ok(ColdCode::Cbor));
assert_eq!(detect_cold_code(0xbf), Ok(ColdCode::Cbor));
}
#[test]
fn detect_msgpack() {
assert_eq!(detect_cold_code(0x80), Ok(ColdCode::MessagePack));
assert_eq!(detect_cold_code(0x8f), Ok(ColdCode::MessagePack));
assert_eq!(detect_cold_code(0xde), Ok(ColdCode::MessagePack));
}
#[test]
fn detect_cesr_binary() {
assert_eq!(detect_cold_code(0xC0), Ok(ColdCode::CesrBinary));
assert_eq!(detect_cold_code(0xFF), Ok(ColdCode::CesrBinary));
}
#[test]
fn detect_unknown() {
assert!(detect_cold_code(0x00).is_err());
}
#[test]
fn tritet_classification() {
assert_eq!(detect_tritet(b'-'), Tritet::CtB64); assert_eq!(detect_tritet(b'{'), Tritet::Json); assert_eq!(detect_tritet(0xE0), Tritet::CtOpB2); assert_eq!(detect_tritet(0x00), Tritet::AnB64); assert_eq!(detect_tritet(0x80), Tritet::Mgpk1); assert_eq!(detect_tritet(0xA0), Tritet::Cbor); assert_eq!(detect_tritet(0xC0), Tritet::Mgpk2); assert_eq!(detect_tritet(b'A'), Tritet::OpB64); assert_eq!(detect_tritet(b'0'), Tritet::CtB64); }
#[test]
fn tritet_to_cold_code() {
assert_eq!(ColdCode::from(Tritet::CtB64), ColdCode::CesrBase64);
assert_eq!(ColdCode::from(Tritet::OpB64), ColdCode::CesrBase64);
assert_eq!(ColdCode::from(Tritet::AnB64), ColdCode::CesrBase64);
assert_eq!(ColdCode::from(Tritet::CtOpB2), ColdCode::CesrBinary);
assert_eq!(ColdCode::from(Tritet::Json), ColdCode::Json);
assert_eq!(ColdCode::from(Tritet::Mgpk1), ColdCode::MessagePack);
assert_eq!(ColdCode::from(Tritet::Mgpk2), ColdCode::MessagePack);
assert_eq!(ColdCode::from(Tritet::Cbor), ColdCode::Cbor);
}
#[test]
fn tritet_all_bytes_covered() {
for byte in 0u8..=255 {
let tritet = detect_tritet(byte);
let _cold: ColdCode = tritet.into();
}
}
}