librqbit-dht 5.3.1

DHT implementation, used in rqbit torrent client.
Documentation
use std::time::Duration;

use anyhow::Context;
use librqbit_core::magnet::Magnet;
use librqbit_dht::DhtBuilder;
use tokio_stream::StreamExt;
use tracing::info;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let magnet = std::env::args()
        .nth(1)
        .expect("first argument should be a magnet link");
    let magnet = Magnet::parse(&magnet).unwrap();
    let info_hash = magnet
        .as_id20()
        .context("Supplied magnet link didn't contain a BTv1 infohash")?;

    tracing_subscriber::fmt::init();

    let dht = DhtBuilder::new().await.context("error initializing DHT")?;

    let mut stream = dht.get_peers(info_hash, None);

    let stats_printer = async {
        loop {
            tokio::time::sleep(Duration::from_secs(5)).await;
            info!("DHT stats: {:?}", dht.stats());
        }
        #[allow(unreachable_code)]
        Ok::<_, anyhow::Error>(())
    };

    let routing_table_dumper = async {
        loop {
            tokio::time::sleep(Duration::from_secs(15)).await;
            dht.with_routing_table(|r| {
                let filename = "/tmp/routing-table.json";
                let mut f = std::fs::OpenOptions::new()
                    .create(true)
                    .write(true)
                    .truncate(true)
                    .open(filename)
                    .unwrap();
                serde_json::to_writer_pretty(&mut f, r).unwrap();
                info!("Dumped DHT routing table to {}", filename);
            });
        }
        #[allow(unreachable_code)]
        Ok::<_, anyhow::Error>(())
    };

    let peer_printer = async {
        while let Some(peer) = stream.next().await {
            info!("peer found: {}", peer)
        }
        Ok(())
    };

    let res = tokio::select! {
        res = stats_printer => res,
        res = peer_printer => res,
        res = routing_table_dumper => res,
    };
    res
}