use std::net::{SocketAddr, SocketAddrV4}; use anyhow::bail; use solstice_proto::server::PeerAddressResponse; use crate::context::Context; use crate::message_handler::MessageHandler; use crate::peer::PeerState; #[derive(Debug, Default)] pub struct PeerAddressResponseHandler; impl MessageHandler for PeerAddressResponseHandler { type Message = PeerAddressResponse; fn run( self, context: &mut Context, response: &PeerAddressResponse, ) -> anyhow::Result<()> { match context.state.peers.peers.get_mut(&response.user_name) { Some(state @ &mut PeerState::FetchingAddress) => { *state = PeerState::Opening { address: SocketAddr::V4(SocketAddrV4::new( response.ip, response.port, )), }; } entry => bail!("unexpected peer state entry: {:?}", entry), }; // TODO: try opening a connection to the peer. Queue an event that will be // handled later if that fails. // // Question: should dispatcher_tx be added to Context? Otherwise, how would // the asynchronous open task notify the context it is done? If we add it, // then how does the dispatcher notice it should stop running? Ok(()) } fn name() -> String { "PeerAddressResponseHandler".to_string() } } #[cfg(test)] mod tests { use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use solstice_proto::server::PeerAddressResponse; use crate::context::{ContextBundle, ContextOptions}; use crate::message_handler::MessageHandler; use crate::peer::PeerState; use super::PeerAddressResponseHandler; #[test] fn run_missing_state() { let mut options = ContextOptions::default(); options.initial_state.peers.peers.insert( "aisha".to_string(), PeerState::Opening { address: "1.2.3.4:42".parse().unwrap(), }, ); let mut bundle = ContextBundle::new(options); let response = PeerAddressResponse { user_name: "aisha".to_string(), ip: Ipv4Addr::new(1, 2, 3, 4), port: 42, }; PeerAddressResponseHandler::default() .run(&mut bundle.context, &response) .unwrap_err(); } #[test] fn run_wrong_state() { let mut bundle = ContextBundle::default(); let response = PeerAddressResponse { user_name: "aisha".to_string(), ip: Ipv4Addr::new(1, 2, 3, 4), port: 42, }; PeerAddressResponseHandler::default() .run(&mut bundle.context, &response) .unwrap_err(); } #[test] fn run_success() { let mut options = ContextOptions::default(); options .initial_state .peers .peers .insert("aisha".to_string(), PeerState::FetchingAddress); let mut bundle = ContextBundle::new(options); let response = PeerAddressResponse { user_name: "aisha".to_string(), ip: Ipv4Addr::new(1, 2, 3, 4), port: 42, }; PeerAddressResponseHandler::default() .run(&mut bundle.context, &response) .unwrap(); match bundle.context.state.peers.peers.get("aisha") { Some(PeerState::Opening { address }) => assert_eq!( address, &SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 42)) ), entry => panic!("unexpected peer state entry: {:?}", entry), }; } }