koibumi-core 0.0.9

The core library for Koibumi, an experimental Bitmessage client
Documentation
use std::{
    fmt,
    io::{self, Read, Write},
};

use crate::{
    io::{LimitedReadFrom, ReadFrom, WriteTo},
    message::Message,
    packet::Command,
    var_type::{VarInt, VarStr},
};

/// A fatality level of an error message.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Fatal(u64);

impl fmt::Display for Fatal {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl Fatal {
    /// Constructs a fatality level from a level value.
    pub fn new(value: u64) -> Self {
        Self(value)
    }

    /// Returns the value as `u64`.
    pub fn as_u64(self) -> u64 {
        self.0
    }
}

impl From<u64> for Fatal {
    fn from(value: u64) -> Self {
        Self(value)
    }
}

impl WriteTo for Fatal {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        VarInt::new(self.0).write_to(w)
    }
}

impl ReadFrom for Fatal {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(VarInt::read_from(r)?.as_u64()))
    }
}

/// A text of an error message.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ErrorText(VarStr);

impl ErrorText {
    /// Constructs an error text from a byte string.
    pub fn new(bytes: Vec<u8>) -> Self {
        Self(VarStr::new(bytes))
    }
}

impl fmt::Display for ErrorText {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<Vec<u8>> for ErrorText {
    fn from(bytes: Vec<u8>) -> Self {
        Self(bytes.into())
    }
}

impl WriteTo for ErrorText {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl LimitedReadFrom for ErrorText {
    fn limited_read_from(r: &mut dyn Read, max_len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(VarStr::limited_read_from(r, max_len)?))
    }
}

/// An "error" message that is issued when some error occurred
/// in a communication.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Error {
    fatal: Fatal,
    ban_time: VarInt,
    // inventory_vector: Inv,
    inventory_vector: VarStr,
    error_text: ErrorText,
}

impl Error {
    /// Constructs an "error" message
    /// from a fatality level and an error text.
    pub fn new(fatal: Fatal, error_text: ErrorText) -> Self {
        Self {
            fatal,
            ban_time: 0u64.into(),
            inventory_vector: b"".to_vec().into(),
            error_text,
        }
    }

    /// Returns the fatality level.
    pub fn fatal(&self) -> Fatal {
        self.fatal
    }

    /// Returns the error text.
    pub fn error_text(&self) -> &ErrorText {
        &self.error_text
    }
}

impl WriteTo for Error {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.fatal.write_to(w)?;
        self.ban_time.write_to(w)?;
        self.inventory_vector.write_to(w)?;
        self.error_text.write_to(w)?;
        Ok(())
    }
}

impl ReadFrom for Error {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        const MAX_VAR_STR_LENGTH: usize = 5000; // XXX

        Ok(Self {
            fatal: Fatal::read_from(r)?,
            ban_time: VarInt::read_from(r)?,
            inventory_vector: VarStr::limited_read_from(r, MAX_VAR_STR_LENGTH)?,
            error_text: ErrorText::limited_read_from(r, MAX_VAR_STR_LENGTH)?,
        })
    }
}

impl Message for Error {
    const COMMAND: Command = Command::ERROR;
}

#[test]
fn test_error_write_to() {
    let test = Error::new(1u64.into(), b"hello".to_vec().into());
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [1, 0, 0, 5, b'h', b'e', b'l', b'l', b'o'];
    assert_eq!(bytes, expected.to_vec());
}

#[test]
fn test_error_read_from() {
    use std::io::Cursor;

    let mut bytes = Cursor::new([1, 0, 0, 5, b'h', b'e', b'l', b'l', b'o']);
    let test = Error::read_from(&mut bytes).unwrap();
    let expected = Error::new(1u64.into(), b"hello".to_vec().into());
    assert_eq!(test, expected);
}