use crate::core::{ ValueDecode, ValueDecodeError, ValueDecoder, ValueEncode, ValueEncodeError, ValueEncoder, }; /*=========* * MESSAGE * *=========*/ const CODE_PIERCE_FIREWALL: u32 = 0; const CODE_PEER_INIT: u32 = 1; /// This enum contains all the possible messages peers can exchange. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Message { PierceFirewall(u32), PeerInit(PeerInit), Unknown(u32), } impl ValueDecode for Message { fn decode_from(decoder: &mut ValueDecoder) -> Result { let position = decoder.position(); let code: u32 = decoder.decode()?; let message = match code { CODE_PIERCE_FIREWALL => { let val = decoder.decode()?; Message::PierceFirewall(val) } CODE_PEER_INIT => { let peer_init = decoder.decode()?; Message::PeerInit(peer_init) } _ => { return Err(ValueDecodeError::InvalidData { value_name: "peer message code".to_string(), cause: format!("unknown value {}", code), position: position, }) } }; Ok(message) } } impl ValueEncode for Message { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { match *self { Message::PierceFirewall(token) => { encoder.encode_u32(CODE_PIERCE_FIREWALL)?; encoder.encode_u32(token)?; } Message::PeerInit(ref request) => { encoder.encode_u32(CODE_PEER_INIT)?; request.encode_to(encoder)?; } Message::Unknown(_) => unreachable!(), } Ok(()) } } /// The type of a connection to a peer. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PeerConnectionType { /// File transfer connection. File, /// Any other connection. Peer, } const CONNECTION_TYPE_FILE: &'static str = "F"; const CONNECTION_TYPE_PEER: &'static str = "P"; impl ValueEncode for PeerConnectionType { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { let string = match *self { Self::File => CONNECTION_TYPE_FILE, Self::Peer => CONNECTION_TYPE_PEER, }; encoder.encode_string(string)?; Ok(()) } } impl ValueDecode for PeerConnectionType { fn decode_from(decoder: &mut ValueDecoder) -> Result { let position = decoder.position(); let string: String = decoder.decode()?; match string.as_ref() { CONNECTION_TYPE_FILE => Ok(Self::File), CONNECTION_TYPE_PEER => Ok(Self::Peer), _ => Err(ValueDecodeError::InvalidData { value_name: "peer connection type".to_string(), cause: format!("unknown value {:?}", string), position: position, }), } } } // TODO: Rename to PeerInitMessage. #[derive(Clone, Debug, Eq, PartialEq)] pub struct PeerInit { pub user_name: String, pub connection_type: PeerConnectionType, pub token: u32, } impl ValueEncode for PeerInit { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.user_name)?; encoder.encode(&self.connection_type)?; encoder.encode_u32(self.token)?; Ok(()) } } impl ValueDecode for PeerInit { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let connection_type = decoder.decode()?; let token = decoder.decode()?; Ok(PeerInit { user_name, connection_type, token, }) } } #[cfg(test)] mod tests { use bytes::BytesMut; use crate::core::testing::roundtrip; use crate::core::{ValueDecodeError, ValueDecoder}; use super::*; #[test] fn invalid_code() { let mut bytes = BytesMut::new(); bytes.extend_from_slice(&[57, 5, 0, 0]); let result = ValueDecoder::new(&bytes).decode::(); assert_eq!( result, Err(ValueDecodeError::InvalidData { value_name: "peer message code".to_string(), cause: "unknown value 1337".to_string(), position: 0, }) ); } #[test] fn invalid_peer_connection_type() { let mut bytes = BytesMut::new(); ValueEncoder::new(&mut bytes).encode("bleep").unwrap(); let result = ValueDecoder::new(&bytes).decode::(); assert_eq!( result, Err(ValueDecodeError::InvalidData { value_name: "peer connection type".to_string(), cause: "unknown value \"bleep\"".to_string(), position: 0, }) ); } #[test] fn roundtrip_pierce_firewall() { roundtrip(Message::PierceFirewall(1337)) } #[test] fn roundtrip_peer_init() { roundtrip(Message::PeerInit(PeerInit { user_name: "alice".to_string(), connection_type: PeerConnectionType::Peer, token: 1337, })); } }