rubit-bencode 0.1.0

The rubit Bittorrent client core lib for all its functions
Documentation
use core::fmt;
use std::{collections::HashMap, process::exit};

use crate::{
    decode::{decode_dict, BencodeTypes},
    unwrap_announce_list, unwrap_dict, unwrap_info_hash, unwrap_integer, unwrap_pieces,
    unwrap_string,
};

pub struct Info {
    pub name: String,
    pub length: u64,
    pub piece_length: u64,
    pub pieces: Vec<[u8; 20]>,
}

pub struct TorrentFile {
    pub info_hash: [u8; 20],
    pub announce: String,
    pub announce_list: Option<Vec<Vec<String>>>,
    pub created_by: Option<String>,
    pub creation_date: Option<u64>,
    pub encoding: Option<String>,
    pub info: Info,
}

fn make_torrent_file<'a>(dict: &'a mut HashMap<String, BencodeTypes>) -> Option<TorrentFile> {
    let info_hash = unwrap_info_hash(dict.remove("info_hash")?)?;
    let announce = unwrap_string(dict.remove("announce")?)?;
    let mut info_dict = unwrap_dict(dict.remove("info")?)?;

    let name = unwrap_string(info_dict.remove("name")?)?;
    let length = unwrap_integer(info_dict.remove("length")?)?;
    let piece_length = unwrap_integer(info_dict.remove("piece length")?)?;
    let pieces = unwrap_pieces(info_dict.remove("pieces")?)?;

    let info = Info {
        name,
        length,
        piece_length,
        pieces,
    };

    let announce_list = match dict.remove("announce-list") {
        Some(l) => unwrap_announce_list(l),
        None => None,
    };

    let created_by = match dict.remove("created by") {
        Some(s) => unwrap_string(s),
        None => None,
    };

    let creation_date = match dict.remove("creation date") {
        Some(i) => unwrap_integer(i),
        None => None,
    };

    let encoding = match dict.remove("encoding") {
        Some(s) => unwrap_string(s),
        None => None,
    };

    Some(TorrentFile {
        info_hash,
        announce,
        announce_list,
        created_by,
        creation_date,
        encoding,
        info,
    })
}

impl From<Vec<u8>> for TorrentFile {
    fn from(buf: Vec<u8>) -> Self {
        let mut pointer = 0;
        let mut dict = match decode_dict(&mut pointer, &buf) {
            Ok(d) => d,
            Err(e) => {
                println!("bad torrent file!: {e:?}");
                exit(1);
            }
        };

        match make_torrent_file(&mut dict) {
            Some(t) => t,
            None => {
                println!("Bad torrent file!");
                println!("Is it a single-file torrent?");
                exit(1);
            }
        }
    }
}

impl fmt::Debug for TorrentFile {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TorrentFile")
            .field("info_hash", &self.info_hash)
            .field("announce", &self.announce)
            .field("announce-list", &self.announce_list)
            .field("info", &self.info)
            .finish()
    }
}

impl fmt::Debug for Info {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TorrentFile")
            .field("name", &self.name)
            .field("length", &self.length)
            .field("piece length", &self.piece_length)
            .field("pieces", &format!("too much to show!"))
            .finish()
    }
}