use bytes::{BufMut, Bytes, BytesMut};
use encoding_rs::WINDOWS_1252;
use thiserror::Error;
use super::{CHAR_MAX, INT_MAX, SHORT_MAX, THREE_MAX, encode_number, encode_string};
#[derive(Error, Debug, PartialEq, Eq)]
pub enum EoWriterError {
#[error("Invalid char value {0} must be between 0 and {CHAR_MAX}")]
InvalidCharValue(i32),
#[error("Invalid short value {0} must be between 0 and {SHORT_MAX}")]
InvalidShortValue(i32),
#[error("Invalid three value {0} must be between 0 and {THREE_MAX}")]
InvalidThreeValue(i32),
#[error("Invalid int value {0} must be between 0 and {INT_MAX}")]
InvalidIntValue(i64),
#[error("{0}")]
Other(String),
}
impl From<String> for EoWriterError {
fn from(s: String) -> Self {
Self::Other(s)
}
}
#[derive(Debug, Default)]
pub struct EoWriter {
data: BytesMut,
string_sanitization_mode: bool,
}
impl EoWriter {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(size: usize) -> Self {
Self {
data: BytesMut::with_capacity(size),
..Default::default()
}
}
pub fn add_byte(&mut self, byte: u8) {
self.data.put_u8(byte);
}
pub fn add_bytes(&mut self, bytes: &[u8]) {
self.data.put_slice(bytes);
}
pub fn add_char(&mut self, char: i32) -> Result<(), EoWriterError> {
if !(0..=CHAR_MAX).contains(&char) {
return Err(EoWriterError::InvalidCharValue(char));
}
let encoded = encode_number(char)?;
self.data.put_slice(&encoded[0..1]);
Ok(())
}
pub fn add_short(&mut self, short: i32) -> Result<(), EoWriterError> {
if !(0..=SHORT_MAX).contains(&short) {
return Err(EoWriterError::InvalidShortValue(short));
}
let encoded = encode_number(short)?;
self.data.put_slice(&encoded[0..2]);
Ok(())
}
pub fn add_three(&mut self, three: i32) -> Result<(), EoWriterError> {
if !(0..=THREE_MAX).contains(&three) {
return Err(EoWriterError::InvalidThreeValue(three));
}
let encoded = encode_number(three)?;
self.data.put_slice(&encoded[0..3]);
Ok(())
}
pub fn add_int(&mut self, int: i32) -> Result<(), EoWriterError> {
let encoded = encode_number(int)?;
self.data.put_slice(&encoded[0..4]);
Ok(())
}
fn sanitize_string(&self, string: &str) -> String {
if self.string_sanitization_mode {
string
.chars()
.map(|c| if c as i32 == 0xff { 0x79 as char } else { c })
.collect()
} else {
string.to_owned()
}
}
pub fn add_string(&mut self, string: &str) {
let string = self.sanitize_string(string);
let (string, _, _) = WINDOWS_1252.encode(&string);
self.data.put_slice(&string);
}
pub fn add_encoded_string(&mut self, string: &str) {
let string = self.sanitize_string(string);
let (mut string, _, _) = WINDOWS_1252.encode(&string);
let string = string.to_mut();
encode_string(&mut *string);
self.data.put_slice(string);
}
pub fn get_string_sanitization_mode(&self) -> bool {
self.string_sanitization_mode
}
pub fn set_string_sanitization_mode(&mut self, mode: bool) {
self.string_sanitization_mode = mode;
}
pub fn to_byte_array(self) -> Bytes {
self.data.freeze()
}
}
#[cfg(test)]
mod tests {
use crate::data::{CHAR_MAX, SHORT_MAX, THREE_MAX, eo_writer::EoWriterError};
use super::EoWriter;
#[test]
fn with_capacity() {
let writer = EoWriter::with_capacity(10);
assert_eq!(writer.data.capacity(), 10);
}
#[test]
fn add_byte() {
let mut writer = EoWriter::with_capacity(1);
writer.add_byte(1);
assert_eq!(&writer.data[..], [1]);
}
#[test]
fn add_char() {
let mut writer = EoWriter::with_capacity(1);
writer.add_char(1).unwrap();
assert_eq!(&writer.data[..], [2]);
}
#[test]
fn add_short() {
let mut writer = EoWriter::with_capacity(2);
writer.add_short(1).unwrap();
assert_eq!(&writer.data[..], [2, 0xfe]);
}
#[test]
fn add_three() {
let mut writer = EoWriter::with_capacity(3);
writer.add_three(1).unwrap();
assert_eq!(&writer.data[..], [2, 0xfe, 0xfe]);
}
#[test]
fn add_int() {
let mut writer = EoWriter::with_capacity(4);
writer.add_int(1).unwrap();
assert_eq!(&writer.data[..], [2, 0xfe, 0xfe, 0xfe]);
}
#[test]
fn add_negative_char() {
let mut writer = EoWriter::with_capacity(1);
let result = writer.add_char(-1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidCharValue(-1));
}
#[test]
fn add_negative_short() {
let mut writer = EoWriter::with_capacity(2);
let result = writer.add_short(-1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidShortValue(-1));
}
#[test]
fn add_negative_three() {
let mut writer = EoWriter::with_capacity(3);
let result = writer.add_three(-1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidThreeValue(-1));
}
#[test]
fn add_negative_int() {
let mut writer = EoWriter::with_capacity(4);
let result = writer.add_int(-1);
assert_eq!(result, Ok(()));
}
#[test]
fn add_large_char() {
let mut writer = EoWriter::with_capacity(1);
let result = writer.add_char(CHAR_MAX + 1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidCharValue(CHAR_MAX + 1));
}
#[test]
fn add_large_short() {
let mut writer = EoWriter::with_capacity(2);
let result = writer.add_short(SHORT_MAX + 1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidShortValue(SHORT_MAX + 1));
}
#[test]
fn add_large_three() {
let mut writer = EoWriter::with_capacity(3);
let result = writer.add_three(THREE_MAX + 1).unwrap_err();
assert_eq!(result, EoWriterError::InvalidThreeValue(THREE_MAX + 1));
}
#[test]
fn add_large_int() {
let mut writer = EoWriter::with_capacity(4);
let result = writer.add_int(-i32::MAX).unwrap_err();
assert_eq!(result, EoWriterError::InvalidIntValue(i32::MAX as i64 * 2));
}
#[test]
fn string_sanitization_mode() {
let mut writer = EoWriter::new();
writer.add_string("ÿ");
assert_eq!(&writer.to_byte_array()[..], &[0xff]);
let mut writer = EoWriter::new();
writer.set_string_sanitization_mode(true);
writer.add_string("ÿ");
assert_eq!(&writer.to_byte_array()[..], &[0x79]);
}
}