lbytes 0.1.1

Incredibly simple byte read/write library
Documentation
mod macros;

use std::{
	io::{Read, Seek, Write},
	string::FromUtf8Error,
};

use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
	#[error("IO Error: {0}")]
	IO(#[from] std::io::Error),
	#[error("Utf8: {0}")]
	FromUtf8(#[from] FromUtf8Error),
}

pub trait BytesReadExt: Read + Seek {
	define_integral_r!(i8, 1);
	define_integral_r!(u8, 1);

	define_integral_r!(i16, 2);
	define_integral_r!(u16, 2);

	define_integral_r!(i32, 4);
	define_integral_r!(u32, 4);

	define_integral_r!(i64, 8);
	define_integral_r!(u64, 8);

	define_integral_r!(i128, 16);
	define_integral_r!(u128, 16);

	fn read_to_vec(&mut self) -> Result<Vec<u8>, Error> {
		let mut buf = Vec::new();
		self.read_to_end(&mut buf)?;
		Ok(buf)
	}

	fn read_n_bytes<const N: usize>(&mut self) -> Result<[u8; N], Error> {
		let mut bytes = [0u8; N];
		self.read_exact(&mut bytes)?;
		Ok(bytes)
	}

	fn read_n_bytes_vec(&mut self, amount: usize) -> Result<Vec<u8>, Error> {
		let mut bytes = vec![0; amount];
		self.read_exact(&mut bytes)?;
		Ok(bytes)
	}

	fn read_f32(&mut self) -> Result<f32, Error> {
		let v = self.read_u32()?;
		Ok(f32::from_bits(v))
	}

	fn read_f64(&mut self) -> Result<f64, Error> {
		let v = self.read_u64()?;
		Ok(f64::from_bits(v))
	}

	fn read_string(&mut self) -> Result<String, Error> {
		let len = self.read_u64()?;
		Ok(String::from_utf8(self.read_n_bytes_vec(len as usize)?)?)
	}
}

pub trait BytesWriteExt: Write {
	define_write!(i8);
	define_write!(u8);

	define_write!(i16);
	define_write!(u16);

	define_write!(i32);
	define_write!(u32);

	define_write!(i64);
	define_write!(u64);

	define_write!(i128);
	define_write!(u128);

	define_write!(f32);
	define_write!(f64);

	fn write_string<S: AsRef<str>>(&mut self, value: S) -> Result<(), Error> {
		let value = value.as_ref();
		self.write_u64(value.len() as u64)?;
		self.write_all(value.as_bytes())?;
		Ok(())
	}
}

impl<R: Read + Seek> BytesReadExt for R {}
impl<R: Write> BytesWriteExt for R {}

#[cfg(test)]
mod tests {
	use std::io::Cursor;

	use super::*;

	#[test]
	fn string() {
		const CONTENTS: &str = "Hello World. Trans Rights 🏳️‍⚧️ 🏳️‍⚧️ 🏳️‍⚧️ 🦀 🦀 🦀";
		let mut buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
		buffer.write_string(CONTENTS.to_string()).expect("Error when writing");
		buffer.set_position(0);
		assert_eq!(CONTENTS, buffer.read_string().expect("Error when reading"));
	}

	macro_rules! define_test {
		($ty:ty) => {
			paste::item! {
				#[test]
				fn [<$ty>]() {
					const VALUES: [$ty; 2] = [$ty::MAX, $ty::MIN];

					for v in VALUES {
						let mut buffer: Cursor<Vec<u8>> =
							Cursor::new(Vec::new());
						buffer.[<write_$ty>](v).expect("Error when writing");
						buffer.set_position(0);
						assert_eq!(
							v,
							buffer.[<read_$ty>]().expect("Error when reading")
						);
					}
				}
			}
		};
	}

	define_test!(i8);
	define_test!(u8);
	define_test!(i16);
	define_test!(u16);
	define_test!(i32);
	define_test!(u32);
	define_test!(i64);
	define_test!(u64);
	define_test!(i128);
	define_test!(u128);
	define_test!(f32);
	define_test!(f64);
}