pub const T_NEW_STREAM: u8 = 0x10;
pub const T_MESSAGE: u8 = 0x11;
pub const T_REQ_NEW_STREAM: u8 = 0x12;
#[derive(PartialEq)]
pub enum Protocol {
NewStream {
#[allow(missing_docs)]
full: bytes::Bytes,
#[allow(missing_docs)]
pub_key: bytes::Bytes,
#[allow(missing_docs)]
base_msg: bytes::Bytes,
#[allow(missing_docs)]
header: bytes::Bytes,
},
Message {
#[allow(missing_docs)]
full: bytes::Bytes,
#[allow(missing_docs)]
pub_key: bytes::Bytes,
#[allow(missing_docs)]
base_msg: bytes::Bytes,
#[allow(missing_docs)]
message: bytes::Bytes,
},
RequestNewStream {
#[allow(missing_docs)]
full: bytes::Bytes,
#[allow(missing_docs)]
pub_key: bytes::Bytes,
#[allow(missing_docs)]
base_msg: bytes::Bytes,
},
}
impl std::fmt::Debug for Protocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NewStream { .. } => {
f.debug_struct("Protocol::NewStream").finish()
}
Self::Message { .. } => {
f.debug_struct("Protocol::Message").finish()
}
Self::RequestNewStream { .. } => {
f.debug_struct("Protocol::RequestNewStream").finish()
}
}
}
}
impl Protocol {
pub fn from_full(full: bytes::Bytes) -> Option<Self> {
if full.len() < 33 {
return None;
}
let pub_key = full.slice(..32);
let base_msg = full.slice(32..);
Some(match full[32] {
T_NEW_STREAM => {
if base_msg.len() != 25 {
return None;
}
let header = full.slice(33..);
Self::NewStream {
full,
pub_key,
base_msg,
header,
}
}
T_MESSAGE => {
let message = full.slice(33..);
Self::Message {
full,
pub_key,
base_msg,
message,
}
}
T_REQ_NEW_STREAM => {
if base_msg.len() != 1 {
return None;
}
Self::RequestNewStream {
full,
pub_key,
base_msg,
}
}
_ => return None,
})
}
pub fn new_stream(pub_key: &[u8], header: &[u8]) -> Self {
let mut out = bytes::BytesMut::with_capacity(32 + 1 + 24);
out.extend_from_slice(&pub_key[..32]);
out.extend_from_slice(&[T_NEW_STREAM]);
out.extend_from_slice(&header[..24]);
Self::from_full(out.freeze()).unwrap()
}
pub fn message(pub_key: &[u8], message: &[u8]) -> Self {
let mut out = bytes::BytesMut::with_capacity(32 + 1 + message.len());
out.extend_from_slice(&pub_key[..32]);
out.extend_from_slice(&[T_MESSAGE]);
out.extend_from_slice(message);
Self::from_full(out.freeze()).unwrap()
}
pub fn request_new_stream(pub_key: &[u8]) -> Self {
let mut out = bytes::BytesMut::with_capacity(32 + 1);
out.extend_from_slice(&pub_key[..32]);
out.extend_from_slice(&[T_REQ_NEW_STREAM]);
Self::from_full(out.freeze()).unwrap()
}
pub fn full(&self) -> &bytes::Bytes {
match self {
Self::NewStream { full, .. } => full,
Self::Message { full, .. } => full,
Self::RequestNewStream { full, .. } => full,
}
}
pub fn pub_key(&self) -> &bytes::Bytes {
match self {
Self::NewStream { pub_key, .. } => pub_key,
Self::Message { pub_key, .. } => pub_key,
Self::RequestNewStream { pub_key, .. } => pub_key,
}
}
pub fn base_msg(&self) -> &bytes::Bytes {
match self {
Self::NewStream { base_msg, .. } => base_msg,
Self::Message { base_msg, .. } => base_msg,
Self::RequestNewStream { base_msg, .. } => base_msg,
}
}
}
#[cfg(test)]
mod test {
use super::*;
const PUB_KEY: &[u8] = &[4; 32];
const HEADER: &[u8] = &[5; 24];
#[inline(always)]
fn valid_roundtrip(orig: &Protocol) {
let new = Protocol::from_full(orig.full().clone()).unwrap();
assert_eq!(orig, &new);
assert_eq!(orig.full(), new.full());
}
#[test]
#[should_panic]
fn bad_pk_size() {
Protocol::request_new_stream(&[4; 31]);
}
#[test]
#[should_panic]
fn bad_hdr_size() {
Protocol::new_stream(PUB_KEY, &[5; 23]);
}
#[test]
fn invalid_type() {
let mut exp_other = bytes::BytesMut::new();
exp_other.extend_from_slice(PUB_KEY);
exp_other.extend_from_slice(&[0x42]);
exp_other.extend_from_slice(b"not a thing");
let exp_other = exp_other.freeze();
assert!(Protocol::from_full(exp_other.clone()).is_none());
}
#[test]
fn new_stream() {
let ns = Protocol::new_stream(PUB_KEY, HEADER);
valid_roundtrip(&ns);
let mut exp_base_msg = Vec::new();
exp_base_msg.push(T_NEW_STREAM);
exp_base_msg.extend_from_slice(HEADER);
assert!(matches!(ns, Protocol::NewStream {
pub_key,
base_msg,
header,
..
} if pub_key.as_ref() == PUB_KEY
&& base_msg.as_ref() == exp_base_msg
&& header.as_ref() == HEADER
));
}
#[test]
fn message() {
let ns = Protocol::message(PUB_KEY, b"hello");
valid_roundtrip(&ns);
let mut exp_base_msg = Vec::new();
exp_base_msg.push(T_MESSAGE);
exp_base_msg.extend_from_slice(b"hello");
assert!(matches!(ns, Protocol::Message {
pub_key,
base_msg,
message,
..
} if pub_key.as_ref() == PUB_KEY
&& base_msg.as_ref() == exp_base_msg
&& message.as_ref() == b"hello"
));
}
#[test]
fn req_new_stream() {
let ns = Protocol::request_new_stream(PUB_KEY);
valid_roundtrip(&ns);
assert!(matches!(ns, Protocol::RequestNewStream {
pub_key,
base_msg,
..
} if pub_key.as_ref() == PUB_KEY && base_msg.as_ref() == [0x12]));
}
}