btpeer 0.11.0

Simple CLI tool and library to get peers from TCP/HTTP and UDP BitTorrent trackers
Documentation
pub mod buffer;
pub mod default;
pub mod i2p;

pub use buffer::Buffer;
pub use default::Default;
pub use i2p::I2p;

use anyhow::{Result, bail};
use cyphernet::addr::{HostName, i2p::I2pAddr};
use std::{
    net::{IpAddr, Ipv4Addr, Ipv6Addr},
    str::FromStr,
};

pub const IP4_LEN: usize = 6;
pub const IP6_LEN: usize = 18;
pub const I2P_LEN: usize = 32;

#[derive(Debug)]
pub enum Peer {
    Default(Default),
    I2p(I2p),
}

impl Peer {
    pub fn from_be_bytes(buffer: &[u8]) -> Result<Self> {
        match buffer.len() {
            IP4_LEN => Self::from_ip4_be_bytes(buffer.try_into()?),
            IP6_LEN => Self::from_ip6_be_bytes(buffer.try_into()?),
            I2P_LEN => Self::from_i2p_be_bytes(buffer.try_into()?),
            l => bail!("unexpected bytes len {l}"),
        }
    }

    pub fn from_ip4_be_bytes(buffer: &[u8; IP4_LEN]) -> Result<Self> {
        const H: usize = 4;
        Ok(Peer::Default(Default {
            host: IpAddr::V4(Ipv4Addr::from({
                let ip_bytes: [u8; H] = buffer[0..H].try_into()?;
                ip_bytes
            })),
            port: u16::from_be_bytes(buffer[H..IP4_LEN].try_into()?),
        }))
    }

    pub fn from_ip6_be_bytes(buffer: &[u8; IP6_LEN]) -> Result<Self> {
        const H: usize = 16;
        Ok(Peer::Default(Default {
            host: IpAddr::V6(Ipv6Addr::from({
                let ip_bytes: [u8; H] = buffer[0..H].try_into()?;
                ip_bytes
            })),
            port: u16::from_be_bytes(buffer[H..IP6_LEN].try_into()?),
        }))
    }

    pub fn from_i2p_be_bytes(buffer: &[u8; I2P_LEN]) -> Result<Self> {
        Ok(Peer::I2p(I2p {
            b32: HostName::I2p(I2pAddr::from_str(&format!(
                "{}.b32.i2p",
                data_encoding::BASE32_NOPAD.encode(buffer)
            ))?)
            .to_string(),
        }))
    }
}

impl std::fmt::Display for Peer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Default(this) => write!(f, "{this}"),
            Self::I2p(this) => write!(f, "{this}"),
        }
    }
}