use crate::in_logic::Logic;
use crate::prelude::BlobError;
use crate::protocol::TransferId;
use crate::protocol_front::{
AckChunkFrontData, ReceiverToSenderFrontCommands, SenderToReceiverFrontCommands,
};
use crate::ChunkIndex;
use err_rs::{ErrorLevel, ErrorLevelProvider};
use log::{debug, trace};
use std::io;
#[derive(Debug)]
pub enum FrontLogicError {
IoError(io::Error),
BlobError(BlobError),
UnknownTransferId(TransferId),
ChunkSizeCanNotBeZero,
}
impl ErrorLevelProvider for FrontLogicError {
fn error_level(&self) -> ErrorLevel {
match self {
Self::IoError(_)
| Self::ChunkSizeCanNotBeZero
| Self::BlobError(_)
| Self::UnknownTransferId(_) => ErrorLevel::Info,
}
}
}
impl From<BlobError> for FrontLogicError {
fn from(err: BlobError) -> Self {
Self::BlobError(err)
}
}
pub struct Info {
pub transfer_id: TransferId,
pub fixed_chunk_size: u16,
pub octet_count: usize,
pub chunk_count_received: u32,
pub waiting_for_chunk_index: ChunkIndex,
}
#[derive(Debug)]
pub struct State {
transfer_id: TransferId,
logic: Logic,
}
#[derive(Debug, Default)]
pub struct FrontLogic {
state: Option<State>,
should_reply_ack: bool,
}
impl FrontLogic {
#[must_use]
pub const fn new() -> Self {
Self {
state: None,
should_reply_ack: false,
}
}
pub fn receive(
&mut self,
command: &SenderToReceiverFrontCommands,
) -> Result<(), FrontLogicError> {
match command {
SenderToReceiverFrontCommands::StartTransfer(start_transfer_data) => {
if self
.state
.as_ref()
.map_or(true, |s| s.transfer_id.0 != start_transfer_data.transfer_id)
{
debug!(
"received a start transfer for {}. sending ack.",
start_transfer_data.transfer_id
);
if start_transfer_data.chunk_size == 0 {
Err(FrontLogicError::ChunkSizeCanNotBeZero)?;
}
self.state = Some(State {
transfer_id: TransferId(start_transfer_data.transfer_id),
logic: Logic::new(
start_transfer_data.total_octet_size as usize,
start_transfer_data.chunk_size,
),
});
self.should_reply_ack = true;
}
Ok(())
}
SenderToReceiverFrontCommands::SetChunk(chunk_data) => {
if let Some(ref mut state) = self.state {
trace!(
"received chunk {} (transfer:{})",
chunk_data.data.chunk_index,
chunk_data.transfer_id.0
);
state.logic.receive(&chunk_data.data)?;
if state.logic.is_complete() {
trace!("received all chunks!");
}
Ok(())
} else {
Err(FrontLogicError::UnknownTransferId(chunk_data.transfer_id))
}
}
}
}
pub fn send(&mut self) -> Option<ReceiverToSenderFrontCommands> {
if self.should_reply_ack {
self.should_reply_ack = false;
let transfer_id = self.state.as_ref()?.transfer_id.0;
Some(ReceiverToSenderFrontCommands::AckStart(transfer_id))
} else if let Some(state) = self.state.as_mut() {
let ack = &state.logic.send();
Some(ReceiverToSenderFrontCommands::AckChunk(AckChunkFrontData {
transfer_id: state.transfer_id,
data: *ack,
}))
} else {
None
}
}
#[must_use]
pub fn blob(&self) -> Option<&[u8]> {
self.state.as_ref().and_then(|state| state.logic.blob())
}
#[must_use]
pub fn info(&self) -> Option<Info> {
self.state.as_ref().map(|s| {
let info = s.logic.info();
Info {
transfer_id: s.transfer_id,
fixed_chunk_size: info.chunk_octet_size,
octet_count: info.total_octet_size,
chunk_count_received: info.chunk_count_received,
waiting_for_chunk_index: info.waiting_for_chunk_index,
}
})
}
}