nardol 0.0.3

Simple framework that provides structure to data sent and received from network.
Documentation
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::net::TcpStream;
use std::path::Path;

use crate::bytes::Bytes;
use crate::error::{Error, ErrorKind};
use crate::packet::{Packet, PacketKind};

/// Sends [`content`](Bytes) via `stream`.
///
/// This method can be used in implementation of [ContentType].
pub fn send_content(stream: &mut TcpStream, content: Bytes) -> Result<(), Error> {
    let content_split = Packet::split_to_max_packet_size(content);
    // Write all content packets to stream.
    for packet_content in content_split.into_iter() {
        let packet = Packet::new(PacketKind::Content, packet_content)?;
        packet.send(stream)?;
    }
    Ok(())
}

/// Receives [`content`](Bytes) and `end_data` [Packet] from `stream`.
///
/// This method can be used in implementation of [ContentType].
pub fn receive_content(stream: &mut TcpStream) -> Result<(Bytes, Packet), Error> {
    let mut content = Bytes::empty();

    // loop until end_data packet is received.
    loop {
        let mut packet = Packet::receive(stream)?;

        match packet.kind() {
            PacketKind::Empty => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got empty.".to_string()),
                ));
            }
            PacketKind::MetaData | PacketKind::MetaDataEnd => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got metadata.".to_string()),
                ));
            }
            PacketKind::Content => {
                content.append(packet.content_mut());
            }
            PacketKind::End => {
                let end_data = packet;
                return Ok((content, end_data));
            }
            PacketKind::Unit => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got unit.".to_string()),
                ));
            }
            PacketKind::Unknown => return Err(Error::new(ErrorKind::UnknownPacketKind, None)),
        }
    }
}

/// Sends a file from [`path`](Path) via a [`stream`](TcpStream).
///
/// This method differs from other send methods as this does not load whole file into memory,
/// but it gradually reads file sends part with size equal to [MAX_CONTENT_SIZE](Packet::max_content_size).
pub fn send_file(stream: &mut TcpStream, path: &Path) -> Result<(), Error> {
    let file = match File::open(path) {
        Ok(file) => file,
        Err(e) => {
            return Err(Error::new(
                ErrorKind::OpeningFileFailed,
                Some(format!("{}", e)),
            ))
        }
    };

    // Get information about to how many packet the file will be split.
    let n_of_content_packets = Packet::number_of_packets(file.metadata().unwrap().len() as usize);

    let mut reader = BufReader::new(file);
    // Starts at 1 and ends inclusively at n_of_content_packets so the whole file is read.
    for part in 1..=n_of_content_packets {
        let mut buff: Vec<u8>;
        if part == n_of_content_packets {
            // Read last part of file.
            buff = Vec::new();
            if let Err(e) = reader.read_to_end(&mut buff) {
                return Err(Error::new(
                    ErrorKind::ReadingFromFileFailed,
                    Some(format!(
                        "Failed to read last content packet from file.\n({})",
                        e
                    )),
                ));
            }
        } else {
            // Create a buffer with exact buffer size.
            buff = vec![0_u8; Packet::max_content_size() as usize];
            // This read_exact instead of read is really important, valid content is
            // guaranteed by getting correct size of file and then checking if end of file was reached.
            if let Err(e) = reader.read_exact(&mut buff) {
                return Err(Error::new(
                    ErrorKind::ReadingFromFileFailed,
                    Some(format!("Failed to read content packet from file.\n({})", e)),
                ));
            }
        }
        let packet = Packet::new(PacketKind::Content, buff.into())?;

        packet.send(stream)?;
    }
    Ok(())
}

/// Receives a file from [`stream`](TcpStream) and saves it to [`location`](Path) with `file_name`.
///
/// This method differs from other receive methods as this does not load whole file into memory,
/// but it gradually receives part of file and saves it.
pub fn receive_file(
    stream: &mut TcpStream,
    location: &Path,
    file_name: String,
) -> Result<(usize, Packet), Error> {
    let mut path = location.to_path_buf();
    path.push(&file_name);

    let file = match File::create(path) {
        Ok(file) => file,
        Err(e) => {
            return Err(Error::new(
                ErrorKind::CreatingFileFailed,
                Some(format!("Could not create a file: {}.\n({})", file_name, e)),
            ));
        }
    };

    let mut writer = BufWriter::new(file);

    let mut number_of_packets = 0;
    // Loop to receive packets until end_data packet comes.
    loop {
        let packet = Packet::receive(stream)?;

        match packet.kind() {
            PacketKind::Empty => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got empty.".to_string()),
                ));
            }
            PacketKind::MetaData | PacketKind::MetaDataEnd => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got metadata.".to_string()),
                ));
            }
            PacketKind::Content => {
                // Write to file.
                if let Err(e) = writer.write(&packet.content_move().into_vec()) {
                    return Err(Error::new(
                        ErrorKind::WritingToFileFailed,
                        Some(format!("Could not write to file. ({})", e)),
                    ));
                }
            }
            PacketKind::End => {
                writer.flush().unwrap();
                let end_data = packet;
                return Ok((number_of_packets, end_data));
            }
            PacketKind::Unit => {
                return Err(Error::new(
                    ErrorKind::InvalidPacketKind,
                    Some("Expected content packet, got unit.".to_string()),
                ));
            }
            PacketKind::Unknown => return Err(Error::new(ErrorKind::UnknownPacketKind, None)),
        }

        number_of_packets += 1;
    }
}