tatami 0.1.4

A library for creating satellites and interacting with Tatami protocols.
Documentation
use libp2p::{identity::Keypair, swarm::NetworkInfo, Multiaddr, PeerId, TransportError, gossipsub::error::PublishError};
use std::io;
use tracing::{info, warn};

use crate::{Satellite, TatamiError};

use super::message::Message;

impl Satellite {
	/// Returns the Satellite's Peer ID.
	pub fn local_peer_id(&self) -> PeerId {
		*self.swarm.0.local_peer_id()
	}

	/// Returns the Satellite's keypair.
	pub fn keypair(&self) -> Keypair {
		self.config.keypair.clone()
	}

	/// Tries to find and connect to the closest peers to the given Peer ID.
	/// This function will not return anything, it simply does the thing.
	pub fn get_closest_peers(&mut self, peer_id: PeerId) {
		info!("Searching for the closest peers to {:?}", peer_id);
		self.swarm
			.0
			.behaviour_mut()
			.kademlia
			.get_closest_peers(peer_id);
	}

	/// Initiate a connection to a known peer with its `PeerId`
	pub fn dial_peer(&mut self, peer_id: PeerId) -> Result<(), libp2p::swarm::DialError> {
		self.swarm.0.dial(peer_id)
	}

	/// Initiate a connection to an unknown peer with its Multiaddress
	pub fn dial_address(&mut self, address: Multiaddr) -> Result<(), libp2p::swarm::DialError> {
		self.swarm.0.dial(address)
	}

	/// Returns the Multiaddresses that the Satellite is listening to connections from
	pub fn listeners(&self) -> Vec<Multiaddr> {
		self.swarm
			.0
			.listeners()
			.map(std::clone::Clone::clone)
			.collect()
	}

	/// Attempts to make the Satellite listen at the given Multiaddress
	pub fn listen_on(&mut self, addr: Multiaddr) -> Result<(), TransportError<io::Error>> {
		self.swarm.0.listen_on(addr)?;
		Ok(())
	}

	/// Bans a `PeerId` so that it cannot connect successfully
	pub fn ban_peer_id(&mut self, peer_id: PeerId) {
		self.swarm.0.ban_peer_id(peer_id);
		self.swarm
			.0
			.behaviour_mut()
			.gossipsub
			.blacklist_peer(&peer_id);
	}

	/// Unbans a `PeerId` that was previously banned with `ban_peer_id`
	pub fn unban_peer_id(&mut self, peer_id: PeerId) {
		self.swarm.0.unban_peer_id(peer_id);
		self.swarm
			.0
			.behaviour_mut()
			.gossipsub
			.remove_blacklisted_peer(&peer_id);
	}

	/// Returns a vector of the connected `PeerIds`
	pub fn connected_peers(&self) -> Vec<PeerId> {
		self.swarm.0.connected_peers().copied().collect()
	}

	/// Attempts to disconnect from a specific `PeerId`
	pub fn disconnect_peer_id(&mut self, peer_id: PeerId) -> Result<(), TatamiError> {
		self.swarm
			.0
			.disconnect_peer_id(peer_id)
			.or(Err(TatamiError::Generic))
	}

	/// Checks if the Satellite is connected to a `PeerId`
	pub fn is_connected(&mut self, peer_id: &PeerId) -> bool {
		self.swarm.0.is_connected(peer_id)
	}

	/// Returns information about the network
	pub fn network_info(&self) -> NetworkInfo {
		self.swarm.0.network_info()
	}

	/// Returns information about the network
	pub fn add_address(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) {
		self.swarm
			.0
			.behaviour_mut()
			.kademlia
			.add_address(peer_id, multiaddr);
	}

	/// Publish a message by supplying the message struct
	pub fn publish_message(&mut self, message: Message) -> Result<(), TatamiError> {
		let step = self.swarm.0.behaviour_mut().gossipsub.publish(
			Self::topic(),
			serde_json::to_vec(&message).or(Err(TatamiError::Generic))?,
		);
		match step {
			Ok(_) => Ok(()),
			Err(error) => {
				if let PublishError::InsufficientPeers = error {
					warn!("Satellite had no peers to send messages to.");
					Ok(())
				} else {	
					warn!("Could not send: {:#?}", error);
				Err(TatamiError::Generic)
				}
			}
		}
	}

	/// Publish a message by supplying the payload of the message
	pub fn publish(&mut self, payload: Vec<u8>) -> Result<(), TatamiError> {
		let message = Message::create(payload, self.keypair())?;
		self.publish_message(message)
	}
}