|
|
|
@ -12,24 +12,43 @@ use tokio::{
|
|
|
|
|
prelude::*,
|
|
|
|
|
io::{
|
|
|
|
|
AsyncWrite,
|
|
|
|
|
AsyncBufRead,
|
|
|
|
|
AsyncRead,
|
|
|
|
|
},
|
|
|
|
|
sync::{
|
|
|
|
|
mpsc,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
use version::Version;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Trait RAE headers implement
|
|
|
|
|
pub trait Header: fmt::Debug
|
|
|
|
|
{
|
|
|
|
|
/// Check bit for the `SuperHeader` of this header
|
|
|
|
|
const CHECK: u16;
|
|
|
|
|
/// Name for the `SuperHeader` of this header's text serialisation format
|
|
|
|
|
const NAME: &'static str;
|
|
|
|
|
/// Compute the hash of this header (used for `SuperHeader<Self>`)
|
|
|
|
|
fn hash(&self) -> crypto::sha256::Sha256Hash;
|
|
|
|
|
/// Any additional comment to be inserted into *text* output formats of `SuperHeader<Self>` only
|
|
|
|
|
#[inline] fn comment(&self) -> &str
|
|
|
|
|
{
|
|
|
|
|
""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a `SuperHeader<Self>` for this header.
|
|
|
|
|
#[inline(always)] fn create_super(&self) -> SuperHeader<Self>
|
|
|
|
|
{
|
|
|
|
|
SuperHeader::new_for(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const RAE_HEADER_BIT: [u8; 4] = *b"RAE0";
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
/// The header before all RAE files.
|
|
|
|
|
pub struct SuperHeader<H: Header>
|
|
|
|
|
pub struct SuperHeader<H: Header+?Sized>
|
|
|
|
|
{
|
|
|
|
|
head: [u8; 4],
|
|
|
|
|
vers: Version,
|
|
|
|
@ -39,7 +58,7 @@ pub struct SuperHeader<H: Header>
|
|
|
|
|
_header: PhantomData<H>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<H: Header> Default for SuperHeader<H>
|
|
|
|
|
impl<H: Header+?Sized> Default for SuperHeader<H>
|
|
|
|
|
{
|
|
|
|
|
#[inline]
|
|
|
|
|
fn default() -> Self
|
|
|
|
@ -50,18 +69,71 @@ impl<H: Header> Default for SuperHeader<H>
|
|
|
|
|
|
|
|
|
|
const MAX_TEXT_SZ: Option<usize> = Some(1024 * 1024);
|
|
|
|
|
|
|
|
|
|
impl<H: Header> SuperHeader<H>
|
|
|
|
|
impl<H: Header +?Sized> SuperHeader<H>
|
|
|
|
|
{
|
|
|
|
|
/// Write this superheader as text bytes to this stream
|
|
|
|
|
#[instrument(err, skip(out))]
|
|
|
|
|
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
|
|
|
|
|
{
|
|
|
|
|
todo!()
|
|
|
|
|
let string = format!(r#"--- {} v{}-{:x} ({}) {} ---
|
|
|
|
|
"#, std::str::from_utf8(&self.head[..])
|
|
|
|
|
.wrap_err_with(|| eyre::eyre!("Invalid head check write"))
|
|
|
|
|
.with_section(|| String::from_utf8_lossy(&self.head[..]).into_owned().header("Head string was"))
|
|
|
|
|
.with_section(|| self.head.fmt_view().to_string().header("Head bytes were"))?,
|
|
|
|
|
self.vers,
|
|
|
|
|
self.vers.to_u32(),
|
|
|
|
|
self.header_hash.to_hex_string(),
|
|
|
|
|
H::NAME);
|
|
|
|
|
out.write_all(string.as_bytes()).await?;
|
|
|
|
|
Ok(string.len())
|
|
|
|
|
}
|
|
|
|
|
/// Read a superheader as text bytes from this stream
|
|
|
|
|
#[instrument(err, skip(input))]
|
|
|
|
|
pub async fn read_text<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
|
|
|
|
|
pub async fn read_text<T: AsyncBufRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
|
|
|
|
|
{
|
|
|
|
|
let mut line = String::new();
|
|
|
|
|
input.read_line(&mut line).await?;
|
|
|
|
|
let line = line.trim();
|
|
|
|
|
trace!("Read super-header line: {:?}", line);
|
|
|
|
|
let mut chunks = line.split_whitespace();
|
|
|
|
|
let mut item = 0;
|
|
|
|
|
macro_rules! take_one {
|
|
|
|
|
() => {
|
|
|
|
|
if let Some(chunk) = chunks.next() {
|
|
|
|
|
chunk
|
|
|
|
|
} else {
|
|
|
|
|
return Err(eyre::eyre!("Not enough information encoded for this string to be valid")
|
|
|
|
|
.with_section(|| format!("{:#?}", chunks).header("Split section was"))
|
|
|
|
|
.with_note(|| "Expect at least 6 items delimited by whitespace"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
macro_rules! check_eq {
|
|
|
|
|
($val:expr) => {
|
|
|
|
|
{
|
|
|
|
|
let val = $val;
|
|
|
|
|
let chunk = take_one!();
|
|
|
|
|
if chunk != val {
|
|
|
|
|
return Err(eyre::eyre!("Invalid data at index {}", item)
|
|
|
|
|
.with_section(|| format!("{:?}", val).header("Expected"))
|
|
|
|
|
.with_section(|| format!("{:?}", chunk).header("Got")));
|
|
|
|
|
}
|
|
|
|
|
item +=1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
check_eq!("---");
|
|
|
|
|
check_eq!(unsafe{std::str::from_utf8_unchecked(&RAE_HEADER_BIT[..])});
|
|
|
|
|
//TODO: Parse text version from hex encoded integer in `2`
|
|
|
|
|
let version = {
|
|
|
|
|
let enc_str = take_one!();
|
|
|
|
|
// Version should be encoded like: v0.0.0*-0 (v<version string>-<version number (hex)>)
|
|
|
|
|
todo!()
|
|
|
|
|
};
|
|
|
|
|
//TODO: Parse hash from hex encoded string `3`
|
|
|
|
|
check_eq!(H::NAME);
|
|
|
|
|
check_eq!("---");
|
|
|
|
|
debug_assert_eq!(item, 6);
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -131,7 +203,7 @@ impl<H: Header> SuperHeader<H>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Verify this empty superheader
|
|
|
|
|
pub fn verify(&self) -> Result<(), VerificationError>
|
|
|
|
|
pub fn verify(&self) -> Result<(), VerificationError<H>>
|
|
|
|
|
{
|
|
|
|
|
macro_rules! check {
|
|
|
|
|
($field:expr, $err:expr) => {
|
|
|
|
@ -142,7 +214,7 @@ impl<H: Header> SuperHeader<H>
|
|
|
|
|
}
|
|
|
|
|
check!(self.head == RAE_HEADER_BIT, VerificationError::BadHeaderBit);
|
|
|
|
|
check!(self.vers.is_compat(&CURRENT_VERSION), VerificationError::IncompatableVersion(self.vers));
|
|
|
|
|
check!(self.chk == H::CHECK, VerificationError::BadCheckBit);
|
|
|
|
|
check!(self.chk == H::CHECK, VerificationError::BadCheckBit(PhantomData));
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
@ -151,7 +223,7 @@ impl<H: Header> SuperHeader<H>
|
|
|
|
|
///
|
|
|
|
|
/// # Notes
|
|
|
|
|
/// This also calls `self.verify()`.
|
|
|
|
|
pub fn verify_for(&self, header: &H) -> Result<(), VerificationError>
|
|
|
|
|
pub fn verify_for(&self, header: &H) -> Result<(), VerificationError<H>>
|
|
|
|
|
{
|
|
|
|
|
self.verify()?;
|
|
|
|
|
if self.header_hash == header.hash() {
|
|
|
|
@ -162,25 +234,29 @@ impl<H: Header> SuperHeader<H>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Error returned when `SuperHeader` fails to verify.
|
|
|
|
|
///
|
|
|
|
|
/// `H` is the header type that we were attempting to verify for
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[non_exhaustive]
|
|
|
|
|
pub enum VerificationError
|
|
|
|
|
pub enum VerificationError<H: Header+?Sized>
|
|
|
|
|
{
|
|
|
|
|
BadHeaderBit,
|
|
|
|
|
IncompatableVersion(Version),
|
|
|
|
|
BadCheckBit,
|
|
|
|
|
BadCheckBit(PhantomData<H>),
|
|
|
|
|
BadHash,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl error::Error for VerificationError{}
|
|
|
|
|
impl fmt::Display for VerificationError
|
|
|
|
|
impl<H: Header+?Sized> error::Error for VerificationError<H>{}
|
|
|
|
|
impl<H: Header+?Sized> fmt::Display for VerificationError<H>
|
|
|
|
|
{
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
|
|
|
{
|
|
|
|
|
write!(f, "failed to verify header for {}: ", std::any::type_name::<H>())?;
|
|
|
|
|
match self {
|
|
|
|
|
Self::BadHeaderBit => write!(f, "bad header bit"),
|
|
|
|
|
Self::IncompatableVersion(vers) => write!(f, "contained incompatable version {}, we are on version {}", vers, CURRENT_VERSION),
|
|
|
|
|
Self::BadCheckBit => write!(f, "contained invalid check bit"),
|
|
|
|
|
Self::BadCheckBit(_) => write!(f, "contained invalid check bit"),
|
|
|
|
|
Self::BadHash => write!(f, "contained invalid header hash"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|