use crate::{
parser::{Parsable, Source},
result::Error,
};
pub fn read_utf8_char<S>(source: &mut Source<S>) -> Result<char, Error>
where
S: Parsable,
{
let first_byte = source.peek1()?;
if first_byte & 0x80 == 0 {
source.advance(1);
return Ok(first_byte as char);
}
let char_len = if first_byte & 0xE0 == 0xC0 {
2
} else if first_byte & 0xF0 == 0xE0 {
3
} else if first_byte & 0xF8 == 0xF0 {
4
} else {
return Err(Error::NoMatch);
};
let bytes = source.read(char_len)?;
let mut code_point = (first_byte & (0xFF >> (char_len + 1))) as u32;
for byte in bytes.iter().take(char_len).skip(1) {
if byte & 0xC0 != 0x80 {
return Err(Error::NoMatch);
}
code_point = (code_point << 6) | ((byte & 0x3F) as u32);
}
let min_code_point = match char_len {
2 => 0x80,
3 => 0x800,
4 => 0x10000,
_ => 0,
};
if code_point < min_code_point {
return Err(Error::NoMatch);
}
char::from_u32(code_point).ok_or(Error::NoMatch)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_read_ascii_char() {
let mut input = Cursor::new(b"Hello");
let mut source = crate::parser::Source::new(&mut input);
let ch = read_utf8_char(&mut source).unwrap();
assert_eq!(ch, 'H');
}
#[test]
fn test_read_two_byte_utf8() {
let mut input = Cursor::new("café".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
read_utf8_char(&mut source).unwrap();
read_utf8_char(&mut source).unwrap();
let ch = read_utf8_char(&mut source).unwrap();
assert_eq!(ch, 'f');
let ch = read_utf8_char(&mut source).unwrap();
assert_eq!(ch, 'é'); }
#[test]
fn test_read_three_byte_utf8() {
let mut input = Cursor::new("世界".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let ch1 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch1, '世');
let ch2 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch2, '界'); }
#[test]
fn test_read_four_byte_utf8() {
let mut input = Cursor::new("👋🌍".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let ch1 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch1, '👋');
let ch2 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch2, '🌍'); }
#[test]
fn test_read_mixed_utf8() {
let mut input = Cursor::new("A世👋".as_bytes());
let mut source = crate::parser::Source::new(&mut input);
let ch1 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch1, 'A');
let ch2 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch2, '世');
let ch3 = read_utf8_char(&mut source).unwrap();
assert_eq!(ch3, '👋'); }
#[test]
fn test_invalid_utf8_start_byte() {
let mut input = Cursor::new(b"\xFF\xFE");
let mut source = crate::parser::Source::new(&mut input);
let result = read_utf8_char(&mut source);
assert!(result.is_err());
}
#[test]
fn test_invalid_continuation_byte() {
let mut input = Cursor::new(b"\xC2\xFF");
let mut source = crate::parser::Source::new(&mut input);
let result = read_utf8_char(&mut source);
assert!(result.is_err());
}
#[test]
fn test_truncated_utf8() {
let mut input = Cursor::new(b"\xC2");
let mut source = crate::parser::Source::new(&mut input);
let result = read_utf8_char(&mut source);
assert!(result.is_err());
}
#[test]
fn test_overlong_encoding() {
let mut input = Cursor::new(b"\xC1\x81");
let mut source = crate::parser::Source::new(&mut input);
let result = read_utf8_char(&mut source);
assert!(result.is_err());
}
#[test]
fn test_empty_input() {
let mut input = Cursor::new(b"");
let mut source = crate::parser::Source::new(&mut input);
let result = read_utf8_char(&mut source);
assert!(result.is_err());
}
}