use std::{
convert::TryFrom,
io::{Cursor, Write},
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{auth::AuthFlavor, Error};
const RPC_VERSION: u32 = 2;
#[derive(Debug, PartialEq)]
pub struct CallBody<T, P>
where
T: AsRef<[u8]>,
{
program: u32,
program_version: u32,
procedure: u32,
auth_credentials: AuthFlavor<T>,
auth_verifier: AuthFlavor<T>,
payload: P,
}
impl<'a> CallBody<&'a [u8], &'a [u8]> {
pub(crate) fn from_cursor(r: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
let rpc_version = r.read_u32::<BigEndian>()?;
if rpc_version != RPC_VERSION {
return Err(Error::InvalidRpcVersion(rpc_version));
}
let program = r.read_u32::<BigEndian>()?;
let program_version = r.read_u32::<BigEndian>()?;
let procedure = r.read_u32::<BigEndian>()?;
let auth_credentials = AuthFlavor::from_cursor(r)?;
let auth_verifier = AuthFlavor::from_cursor(r)?;
let data = *r.get_ref();
let start = r.position() as usize;
if start > data.len() {
return Err(Error::IncompleteHeader);
}
let payload = &data[start..];
Ok(CallBody {
program,
program_version,
procedure,
auth_credentials,
auth_verifier,
payload,
})
}
}
impl<T, P> CallBody<T, P>
where
T: AsRef<[u8]>,
P: AsRef<[u8]>,
{
pub fn new(
program: u32,
program_version: u32,
procedure: u32,
auth_credentials: AuthFlavor<T>,
auth_verifier: AuthFlavor<T>,
payload: P,
) -> Self {
Self {
program,
program_version,
procedure,
auth_credentials,
auth_verifier,
payload,
}
}
pub fn serialise_into<W: Write>(&self, mut buf: W) -> Result<(), std::io::Error> {
buf.write_u32::<BigEndian>(RPC_VERSION)?;
buf.write_u32::<BigEndian>(self.program)?;
buf.write_u32::<BigEndian>(self.program_version)?;
buf.write_u32::<BigEndian>(self.procedure)?;
self.auth_credentials.serialise_into(&mut buf)?;
self.auth_verifier.serialise_into(&mut buf)?;
buf.write_all(self.payload.as_ref())
}
pub fn serialised_len(&self) -> u32 {
let mut l = std::mem::size_of::<u32>() * 4;
l += self.auth_credentials.serialised_len() as usize;
l += self.auth_verifier.serialised_len() as usize;
l += self.payload.as_ref().len();
l as u32
}
pub fn rpc_version(&self) -> u32 {
2
}
pub fn program(&self) -> u32 {
self.program
}
pub fn program_version(&self) -> u32 {
self.program_version
}
pub fn procedure(&self) -> u32 {
self.procedure
}
pub fn auth_credentials(&self) -> &AuthFlavor<T> {
&self.auth_credentials
}
pub fn auth_verifier(&self) -> &AuthFlavor<T> {
&self.auth_verifier
}
pub fn payload(&self) -> &P {
&self.payload
}
}
impl<'a> TryFrom<&'a [u8]> for CallBody<&'a [u8], &'a [u8]> {
type Error = Error;
fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
let mut c = Cursor::new(v);
CallBody::from_cursor(&mut c)
}
}
#[cfg(feature = "bytes")]
impl TryFrom<crate::Bytes> for CallBody<crate::Bytes, crate::Bytes> {
type Error = Error;
fn try_from(mut v: crate::Bytes) -> Result<Self, Self::Error> {
use crate::{bytes_ext::BytesReaderExt, Buf};
let rpc_version = v.try_u32()?;
if rpc_version != RPC_VERSION {
return Err(Error::InvalidRpcVersion(rpc_version));
}
let program = v.try_u32()?;
let program_version = v.try_u32()?;
let procedure = v.try_u32()?;
let auth_credentials = AuthFlavor::try_from(v.clone())?;
v.advance(auth_credentials.serialised_len() as usize);
let auth_verifier = AuthFlavor::try_from(v.clone())?;
v.advance(auth_verifier.serialised_len() as usize);
Ok(Self {
program,
program_version,
procedure,
auth_credentials,
auth_verifier,
payload: v,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_differing_payload_type() {
let binding = vec![42];
let auth = AuthFlavor::AuthNone(Some(binding.as_slice()));
let payload = [42, 42, 42, 42];
let _call: CallBody<&[u8], &[u8; 4]> =
CallBody::new(100000, 42, 13, auth.clone(), auth, &payload);
}
}