1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
#![deny(missing_docs)]
//! This create provides important functions for reading and writing data
use std::future::Future;
use std::io::{Read, Write};
use tokio::io::{AsyncWrite, AsyncRead};
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
/// Reads a single byte asyncronously from the provided reader
///
/// # Arguments
///
/// `reader` - A mutable reference to a type implemnting `AsyncRead` and `Unpin`
/// `line` - A line number, where the function is called
/// `file` - A static string slice containing the name of the file, where the function was called
///
/// # Returns
///
/// Returns an `binary_utils::Result` containing the byte or an error if the reading failed
///
/// # Errors
///
/// This function can fail, if there is no data in the provided `reader` to read.
pub async fn read_byte(reader: &mut (impl AsyncRead + Unpin), line: u32, file: &'static str) -> Result<u8> {
let mut data = [0u8; 1];
match reader.read_exact(&mut data).await {
Ok(_) => Ok(data[0]),
Err(_) => Error::NotEnoughtBytes(format!("{}:{}", file, line)).into(),
}
}
/// Consumes a single utf16be character asyncronously from the provided reader
///
/// This is intended to be used to move the cursor over an utf16be string.
///
/// # Arguments
///
/// `reader` - A mutable reference to a type implemnting `AsyncRead` and `Unpin`
/// `line` - A line number, where the function is called
/// `file` - A static string slice containing the name of the file, where the function was called
///
/// # Returns
///
/// Returns an `binary_utils::Result` containing `Ok(())` or an error if the reading failed
///
/// # Errors
///
/// This function can fail, if there is no data in the provided `reader` to read.
pub async fn consume_utf16be_char(reader: &mut (impl AsyncRead + Unpin), line: u32, file: &'static str) -> Result<()> {
read_byte(reader, line, file).await?;
read_byte(reader, line, file).await?;
Ok(())
}
/// A function to asyncronously write data to the provided `writer`.
///
/// # Arguments
///
/// `writer` - A mutable reference to a type implementing `AsyncWrite` and `Unpin`
/// `bytes` - A reference to a slice of bytes that gets written into the provided `writer`
///
/// # Returns
///
/// Returns `Ok(())` or an error, if the write operation fails.
pub async fn write_bytes(writer: &mut (impl AsyncWrite + Unpin), bytes: &[u8]) -> Result<()> {
match writer.write_all(bytes).await {
Ok(_) => Ok(()),
Err(_) => Error::FailedToWrite.into(),
}
}
/// Reads a single UTF-8 character asyncronously from the provided reader
///
/// # Arguments
///
/// `reader` - A mutable reference to a type implemnting `AsyncRead` and `Unpin`
/// `line` - A line number, where the function is called
/// `file` - A static string slice containing the name of the file, where the function was called
///
/// # Returns
///
/// Returns an `binary_utils::Result` containing the UTF-8 `char` or an error if the reading failed
///
/// # Errors
///
/// This function can fail, if there is no data in the provided `reader` to read.
pub async fn read_utf8_char(reader: &mut (impl AsyncRead + Unpin), line: u32, file: &'static str) -> Result<char> {
let current = read_byte(reader, line, file).await? as u8;
match current {
0b0000_0000..=0b0111_1111 =>
match char::from_u32(current as u32)
{ Some(v) => Ok(v), None => Error::InvalidStructure.into()},
0b1100_0000..=0b1101_1111 =>
match char::from_u32(((current as u32) << 8) + read_byte(reader, line, file).await? as u32)
{ Some(v) => Ok(v), None => Error::InvalidStructure.into()},
0b1110_0000..=0b1110_1111 =>
match char::from_u32(((current as u32) << 16) + ((read_byte(reader, line, file).await? as u32) << 8) + read_byte(reader, line, file).await? as u32)
{ Some(v) => Ok(v), None => Error::InvalidStructure.into()},
0b1111_0000..=0b1111_0111 =>
match char::from_u32(((current as u32) << 24) + ((read_byte(reader, line, file).await? as u32) << 16) + ((read_byte(reader, line, file).await? as u32) << 8) + read_byte(reader, line, file).await? as u32)
{ Some(v) => Ok(v), None => Error::InvalidStructure.into()},
_ => Error::InvalidStructure.into()
}
}
/// A trait for types that need to be read from a binary stream
///
/// Implementations of this trait provide a way to write their data
/// asyncronously to a type that implements `AsyncRead` and `Unpin`
///
/// # Notes
///
/// This trait is intended to be implemented for types that represent structured data and provide
/// a method to asynchronously read that data from an input source. Implementations should handle
/// error conditions appropriately and return a `Result` indicating success or failure.
///
pub trait DataReader: Sized {
// Reads data from the provided reader asynchronously and constructs an instance of `Self`.
///
/// # Arguments
///
/// * `reader` - A mutable reference to a type implementing `AsyncRead` and `Unpin` traits,
/// from which the data will be read.
///
/// # Returns
///
/// Returns a future representing the asynchronous reading operation. The future resolves to
/// a `Result` containing an instance of `Self` if the operation is successful, or an error
/// indicating failure.
fn read(reader: &mut (impl AsyncRead + Unpin)) -> impl Future<Output = Result<Self>>;
}
/// A trait for types that need to be read from a binary stream synchronously
///
/// Implementations of this trait provide a way to read their data
/// synchronously from a type that implements `Read`
pub trait SyncDataReader: Sized {
/// Reads data from the provided reader synchronously and constructs an instance of `Self`.
///
/// # Arguments
///
/// * `reader` - A mutable reference to a type implementing `Read` trait,
/// from which the data will be read.
///
/// # Returns
///
/// Returns a result containing an instance of `Self` if the operation is successful, or an error
/// indicating failure.
fn read(reader: &mut impl Read) -> Result<Self>;
}
/// A trait for types that have to read Packet data
pub trait PacketReader: Sized {
/// Reads a packet from the provided stream `reader` using the specified `id` and `length`
///
/// # Arguments
///
/// `reader` - A mutable reference to a type implementing `AsyncRead` and `Unpin`
/// `length` - the length of the packet (be careful, could be userdefined)
/// `packet_id` - the id of the packet (packet type)
fn read(
reader: &mut (impl AsyncRead + Unpin),
length: i32,
packet_id: i32,
) -> impl Future<Output = Result<Self>>;
}
/// A trait for types that have to be writen to an asyncronous stream
///
/// # Notes
///
/// This trait is intended to be implemented for types that represent structured data and provide
/// a method to asynchronously write that data into an output source. Implementations should handle
/// error conditions appropriately and return a `Result` indicating success or failure.
///
pub trait DataWriter {
/// Writes the data of the object into the defined `writer`
///
/// # Arguments
///
/// `writer` - A mutable reference to a type implementing `AsyncRead` and `Unpin`
///
/// # Returns
///
/// Returns a future representing the asynchronous writing operation. The future resolves to
/// a `Result` containing an instance of `()` if the operation is successful, or an error
/// indicating failure.
fn write(&self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = Result<()>>;
}
/// A trait for types that have to be written to a synchronous stream
///
/// # Notes
///
/// This trait is intended to be implemented for types that represent structured data and provide
/// a method to synchronously write that data into an output source. Implementations should handle
/// error conditions appropriately and return a `Result` indicating success or failure.
pub trait SyncDataWriter {
/// Writes the data of the object into the defined `writer`.
///
/// # Arguments
///
/// * `writer` - A mutable reference to a type implementing `Write` trait,
/// into which the data will be written.
///
/// # Returns
///
/// Returns a result indicating success or failure of the writing operation.
fn write(&self, writer: &mut impl Write) -> Result<()>;
}
/// A trait for types that need to be read from a binary stream but have an context based size
///
/// Implementations of this trait provide a way to write their data
/// asyncronously to a type that implements `AsyncRead` and `Unpin`
///
/// # Notes
///
/// This trait is intended to be implemented for types that represent structured data and provide
/// a method to asynchronously read that data from an input source. Implementations should handle
/// error conditions appropriately and return a `Result` indicating success or failure.
///
pub trait ListDataReader: Sized {
// Reads data from the provided reader asynchronously and constructs an instance of `Self`.
///
/// # Arguments
///
/// * `reader` - A mutable reference to a type implementing `AsyncRead` and `Unpin` traits,
/// from which the data will be read.
/// * `length` - A length, how much data is contained, because the size is context based
///
/// # Returns
///
/// Returns a future representing the asynchronous reading operation. The future resolves to
/// a `Result` containing an instance of `Self` if the operation is successful, or an error
/// indicating failure.
fn read_list(reader: &mut (impl AsyncRead + Unpin), length: usize) -> impl Future<Output = Result<Self>>;
}
/// A trait implemented by every Packet to make them Storeable and recogniseable
pub trait Packet: Sized {}
/// An enum containing the different variants, why the read and write functions could fail
#[derive(Debug)]
pub enum Error {
/// Used if an id was read that was not expected
InvalidId,
/// Used if the read structure dooes not fit the expected one
InvalidStructure,
/// Used if there are not enough bytes in the stream to read the expected data
NotEnoughtBytes(String),
/// Used if the Write Operation failed
FailedToWrite,
}
/// A type alias for `std::result::Result` with the error type set to `Error`.
///
/// This type is commonly used throughout the codebase to represent the result of operations
/// that may fail, where `T` represents the success value and `Error` represents the type of error.
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
/// Converts the `Error` into a `Result` with an error value.
///
/// This method is useful for converting an `Error` into a `Result` for functions
/// or operations that return a `Result`.
pub fn to_result<T>(self) -> Result<T> {
Err(self)
}
}
impl<T> Into<Result<T>> for Error {
fn into(self) -> Result<T> {
Err(self)
}
}