use brec_consts::*;
use thiserror::Error;
#[cfg(feature = "crypt")]
use crate::crypt::CryptError;
#[cfg(feature = "csharp")]
use crate::csharp_feat::CSharpError;
#[cfg(feature = "java")]
use crate::java_feat::JavaError;
#[cfg(feature = "observer")]
use crate::storage::SensorError;
#[cfg(feature = "wasm")]
use crate::wasm_feat::WasmError;
#[cfg(feature = "napi")]
use brec_node_lib::NapiError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UnrecognizedSignature {
Block([u8; 4]),
Payload(Vec<u8>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Unrecognized {
pub sig: UnrecognizedSignature,
pub pos: Option<u64>,
pub len: Option<u64>,
}
impl Default for Unrecognized {
fn default() -> Self {
Self {
sig: UnrecognizedSignature::Block([0; 4]),
pos: None,
len: None,
}
}
}
impl Unrecognized {
pub fn block(sig: [u8; 4]) -> Self {
Self {
sig: UnrecognizedSignature::Block(sig),
..Self::default()
}
}
pub fn payload(sig: Vec<u8>) -> Self {
Self {
sig: UnrecognizedSignature::Payload(sig),
..Self::default()
}
}
pub fn block_from<T: std::io::Read>(buf: &mut T) -> Result<Self, Error> {
let mut sig = [0u8; BLOCK_SIG_LEN];
let read_sig = read_exact_partial(buf, &mut sig)?;
if read_sig < BLOCK_SIG_LEN {
return Err(Error::NotEnoughtSignatureData(read_sig, BLOCK_SIG_LEN));
}
#[cfg(not(feature = "resilient"))]
{
Ok(Self::block(sig))
}
#[cfg(feature = "resilient")]
{
let mut unrecognized = Self::block(sig);
let mut blk_len = [0u8; BLOCK_SIZE_FIELD_LEN];
let read_len = read_exact_partial(buf, &mut blk_len)?;
if read_len < BLOCK_SIZE_FIELD_LEN {
return Err(Error::NotEnoughData(BLOCK_SIZE_FIELD_LEN - read_len));
}
unrecognized.len = Some(u32::from_le_bytes(blk_len) as u64);
Ok(unrecognized)
}
}
pub fn block_from_slice(buf: &[u8]) -> Result<Self, Error> {
if buf.len() < BLOCK_SIG_LEN {
return Err(Error::NotEnoughtSignatureData(buf.len(), BLOCK_SIG_LEN));
}
let sig = <[u8; BLOCK_SIG_LEN]>::try_from(&buf[..BLOCK_SIG_LEN])?;
#[cfg(not(feature = "resilient"))]
{
Ok(Self::block(sig))
}
#[cfg(feature = "resilient")]
{
let from = BLOCK_SIG_LEN;
let to = BLOCK_SIG_LEN + BLOCK_SIZE_FIELD_LEN;
if buf.len() < to {
return Err(Error::NotEnoughData(to - buf.len()));
}
let blk_len = <[u8; BLOCK_SIZE_FIELD_LEN]>::try_from(&buf[from..to])?;
let mut unrecognized = Self::block(sig);
unrecognized.len = Some(u32::from_le_bytes(blk_len) as u64);
Ok(unrecognized)
}
}
pub fn block_from_buffer<T: std::io::BufRead>(buf: &mut T) -> Result<Self, Error> {
let bytes = buf.fill_buf()?;
Self::block_from_slice(bytes)
}
}
fn read_exact_partial<T: std::io::Read>(
buf: &mut T,
dst: &mut [u8],
) -> Result<usize, std::io::Error> {
let mut total = 0usize;
while total < dst.len() {
match buf.read(&mut dst[total..]) {
Ok(0) => break,
Ok(n) => total += n,
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
Ok(total)
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Not enought data; required = {0}")]
NotEnoughData(usize),
#[error("Not enought data to read signature; data len = {0}; required = {1}")]
NotEnoughtSignatureData(usize, usize),
#[error("Invalid data align; data len = {0}; required = {1}; offset = {2} (expected 0)")]
InvalidAlign(usize, usize, usize),
#[error("Invalid buffer capacity: {0}; expected: {1}")]
InvalidCapacity(usize, String),
#[error("TryFromSliceError: {0}")]
TryFromSliceError(#[from] std::array::TryFromSliceError),
#[error("Signature doesn't match to target entity")]
SignatureDismatch(Unrecognized),
#[error("Crc doesn't match to target entity")]
CrcDismatch,
#[error("Same rule has been added already")]
RuleDuplicate,
#[error("Block has zero length")]
ZeroLengthBlock,
#[error("Invalid encoded length")]
InvalidLength,
#[error("Attempt to read more blocks than allowed")]
MaxBlocksCount,
#[error("Misaligned slice pointer")]
MisalignedPointer,
#[error("Unexpected slice length")]
UnexpectedSliceLength,
#[error("Fail converting \"{0}\" with error: {1}")]
FailedConverting(String, String),
#[error("IO Error: {0}")]
Io(#[from] std::io::Error),
#[error("Fail to exctract data from vector for ByteBlock")]
FailExtractByteBlock,
#[error("Fail to read payload header")]
FailToReadPayloadHeader,
#[error("Memory allocation failed")]
MemoryAllocationFailed,
#[error("Encoding error: {0}")]
EncodeError(String),
#[error("No pending packet to accept")]
NoPendingPacket,
#[error("Fail to read packet header")]
FailToReadPacketHeader,
#[error("PacketBufReader fall down into invalid logic")]
InvalidPacketReaderLogic,
#[error("Fail to find free slot")]
CannotFindFreeSlot,
#[error("Fail to find free palce in slot")]
CannotFindFreePlaceInSlot,
#[error("Fail to insert data into slot")]
CannotInsertIntoSlot,
#[error("Damaged slot: {0}")]
DamagedSlot(Box<Error>),
#[error("Too many attempts to read block; made {0} attempts")]
TooManyAttemptsToReadBlock(usize),
#[error("Out of bounds; len = {0}; requested = {1}")]
OutOfBounds(usize, usize),
#[error("Path isn't a file: {0}")]
PathIsNotFile(String),
#[error("File is locked: {0}")]
FileIsLocked(String),
#[error("Timeout error. File is locked: {0}")]
TimeoutToWaitLockedFile(String),
#[error("Fail to lock file: {0}")]
FailToLockFile(std::io::Error),
#[error("Fail to access slot:{0}")]
AccessSlot(usize),
#[error("Empty source")]
EmptySource,
#[cfg(feature = "crypt")]
#[error("Crypt: {0}")]
Crypt(#[from] CryptError),
#[cfg(feature = "observer")]
#[error("Sensor: {0}")]
Sensor(SensorError),
#[cfg(feature = "observer")]
#[error("No subscription")]
NoSubscription,
#[cfg(feature = "napi")]
#[error("Napi: {0}")]
Napi(#[from] NapiError),
#[cfg(feature = "java")]
#[error("Java: {0}")]
Java(#[from] JavaError),
#[cfg(feature = "csharp")]
#[error("CSharp: {0}")]
CSharp(#[from] CSharpError),
#[cfg(feature = "wasm")]
#[error("Wasm: {0}")]
Wasm(#[from] WasmError),
#[error("Test error has been fired")]
Test,
}
impl Error {
pub fn into_read_status<T>(self) -> Result<crate::ReadStatus<T>, Self> {
match self {
Self::NotEnoughtSignatureData(len, required) => {
Ok(crate::ReadStatus::NotEnoughData((required - len) as u64))
}
Self::NotEnoughData(needed) => Ok(crate::ReadStatus::NotEnoughData(needed as u64)),
err => Err(err),
}
}
}
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
std::io::Error::other(value.to_string())
}
}
#[cfg(feature = "observer")]
impl From<SensorError> for Error {
fn from(value: SensorError) -> Self {
Error::Sensor(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{BufReader, Cursor};
#[test]
fn unrecognized_constructors_set_expected_fields() {
let blk = Unrecognized::block(*b"ABCD");
assert_eq!(blk.sig, UnrecognizedSignature::Block(*b"ABCD"));
assert_eq!(blk.pos, None);
#[cfg(not(feature = "resilient"))]
assert_eq!(blk.len, None);
let payload = Unrecognized::payload(vec![1, 2, 3]);
assert_eq!(payload.sig, UnrecognizedSignature::Payload(vec![1, 2, 3]));
assert_eq!(payload.pos, None);
assert_eq!(payload.len, None);
}
#[test]
fn block_from_slice_and_buffer_parse_signature() {
#[cfg(feature = "resilient")]
let bytes = {
let mut bytes = b"ABCD".to_vec();
bytes.extend_from_slice(&7_u32.to_le_bytes());
bytes
};
#[cfg(not(feature = "resilient"))]
let bytes = b"ABCD".to_vec();
let from_slice = Unrecognized::block_from_slice(&bytes).expect("block_from_slice");
assert_eq!(from_slice.sig, UnrecognizedSignature::Block(*b"ABCD"));
#[cfg(feature = "resilient")]
assert_eq!(from_slice.len, Some(7));
#[cfg(not(feature = "resilient"))]
assert_eq!(from_slice.len, None);
let mut reader = BufReader::new(Cursor::new(bytes));
let from_buffer = Unrecognized::block_from_buffer(&mut reader).expect("block_from_buffer");
assert_eq!(from_buffer.sig, UnrecognizedSignature::Block(*b"ABCD"));
}
#[test]
fn block_from_slice_and_stream_validate_input_size() {
let err = Unrecognized::block_from_slice(&[1, 2, 3]).expect_err("must fail");
assert!(matches!(
err,
Error::NotEnoughtSignatureData(got, needed) if got == 3 && needed == BLOCK_SIG_LEN
));
let mut stream = Cursor::new(vec![1, 2, 3]);
let err = Unrecognized::block_from(&mut stream).expect_err("must fail");
assert!(matches!(
err,
Error::NotEnoughtSignatureData(got, needed) if got == 3 && needed == BLOCK_SIG_LEN
));
}
#[test]
fn into_read_status_maps_only_partial_data_errors() {
match Error::NotEnoughData(5).into_read_status::<u8>() {
Ok(crate::ReadStatus::NotEnoughData(needed)) => assert_eq!(needed, 5),
Ok(crate::ReadStatus::Success(_)) => panic!("expected NotEnoughData"),
Err(_) => panic!("expected Ok(NotEnoughData)"),
}
match Error::NotEnoughtSignatureData(2, 8).into_read_status::<u8>() {
Ok(crate::ReadStatus::NotEnoughData(needed)) => assert_eq!(needed, 6),
Ok(crate::ReadStatus::Success(_)) => panic!("expected NotEnoughData"),
Err(_) => panic!("expected Ok(NotEnoughData)"),
}
assert!(matches!(
Error::RuleDuplicate.into_read_status::<u8>(),
Err(Error::RuleDuplicate)
));
}
#[test]
fn error_converts_to_io_error_with_display_message() {
let io: std::io::Error = Error::CannotFindFreeSlot.into();
assert_eq!(io.kind(), std::io::ErrorKind::Other);
assert!(io.to_string().contains("Fail to find free slot"));
}
}