From 1dcf4f6a37a78c9d36d746452271955325d77879 Mon Sep 17 00:00:00 2001 From: Faerbit Date: Mon, 22 Jul 2024 22:51:46 +0200 Subject: [PATCH] Read actual torrent file and "pretty" print it --- src/bencode.rs | 100 +++++++++++++++++++++++++++++++++---------------- src/main.rs | 21 ++++++++++- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/bencode.rs b/src/bencode.rs index 8502439..fa39df9 100644 --- a/src/bencode.rs +++ b/src/bencode.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::fmt::{Display, Formatter}; use anyhow::{anyhow, ensure, Result}; @@ -24,7 +25,19 @@ struct Torrent { #[derive(Debug, PartialEq, Eq, Hash)] pub struct ByteString(Vec); +impl Display for ByteString { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Ok(str) = std::str::from_utf8(&*self.0) { + write!(f, "{}", str) + } else { + let str_ints: Vec<_> = self.0.iter().map(|x| x.to_string()).collect(); + write!(f, "{{{}}}", str_ints.join(", ")) + } + } +} + impl ByteString { + #[allow(dead_code)] pub fn from_str(input: &str) -> Self { ByteString(input.as_bytes().to_vec()) } @@ -34,14 +47,6 @@ impl ByteString { } } -#[derive(Debug, PartialEq)] -pub enum Bencode { - Integer(i64), - Bytes(ByteString), - List(Vec), - Dict(HashMap), -} - #[derive(Debug, PartialEq)] enum BencodeType { Integer, @@ -50,9 +55,38 @@ enum BencodeType { Dict, } +#[derive(Debug, PartialEq)] +pub enum Bencode { + Integer(i64), + Bytes(ByteString), + List(Vec), + Dict(HashMap), +} + +impl Display for Bencode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Bencode::Integer(i) => { write!(f, "{{i: {}}}", i) } + Bencode::Bytes(b) => { write!(f, "{{b: {}}}", b) } + Bencode::List(l) => { + let strs: Vec<_> = l.iter().map(|x| x.to_string()).collect(); + write!(f, "{{l: [\n{}\n]}}", strs.join(",\n")) + } + Bencode::Dict(d) => { + write!(f, "{{d: {{\n")?; + for (k, v) in d { + write!(f, " {k} : {v}\n")?; + } + write!(f, "\n}} }}") + } + } + + } +} + impl Bencode { - pub fn decode(input: &str) -> Result { - let (result, end_pos) = Self::decode_type(input.as_bytes())?; + pub fn decode(input: &[u8]) -> Result { + let (result, end_pos) = Self::decode_type(input)?; ensure!(end_pos == input.len() - 1, "Could not fully decode input. Got {} chars left to decode", input.len() - 1 - end_pos); Ok(result) @@ -188,9 +222,9 @@ mod tests { #[test] fn test_integer_str() { - assert_eq!(Bencode::decode("i42e").unwrap(), Bencode::Integer(42)); - assert_eq!(Bencode::decode("i17e").unwrap(), Bencode::Integer(17)); - assert_eq!(Bencode::decode("i-17e").unwrap(), Bencode::Integer(-17)); + assert_eq!(Bencode::decode("i42e".as_bytes()).unwrap(), Bencode::Integer(42)); + assert_eq!(Bencode::decode("i17e".as_bytes()).unwrap(), Bencode::Integer(17)); + assert_eq!(Bencode::decode("i-17e".as_bytes()).unwrap(), Bencode::Integer(-17)); } #[test] @@ -206,10 +240,10 @@ mod tests { #[test] fn test_bytes_str() { - assert_eq!(Bencode::decode(test_encode_bytes("hallo").as_str()).unwrap(), Bencode::Bytes(ByteString::from_str("hallo"))); - assert_eq!(Bencode::decode(test_encode_bytes("tschüss").as_str()).unwrap(), Bencode::Bytes(ByteString::from_str("tschüss"))); - assert_eq!(Bencode::decode(test_encode_bytes("💩").as_str()).unwrap(), Bencode::Bytes(ByteString::from_str("💩"))); - assert_eq!(Bencode::decode(test_encode_bytes("hallo 💩, this is a long text").as_str()).unwrap(), Bencode::Bytes(ByteString::from_str("hallo 💩, this is a long text"))); + assert_eq!(Bencode::decode(test_encode_bytes("hallo").as_bytes()).unwrap(), Bencode::Bytes(ByteString::from_str("hallo"))); + assert_eq!(Bencode::decode(test_encode_bytes("tschüss").as_bytes()).unwrap(), Bencode::Bytes(ByteString::from_str("tschüss"))); + assert_eq!(Bencode::decode(test_encode_bytes("💩").as_bytes()).unwrap(), Bencode::Bytes(ByteString::from_str("💩"))); + assert_eq!(Bencode::decode(test_encode_bytes("hallo 💩, this is a long text").as_bytes()).unwrap(), Bencode::Bytes(ByteString::from_str("hallo 💩, this is a long text"))); } #[test] @@ -306,30 +340,30 @@ mod tests { #[test] fn test_list_str() { assert_eq!(Bencode::decode( - "le").unwrap(), + "le".as_bytes()).unwrap(), Bencode::List(Vec::new()) ); assert_eq!(Bencode::decode( - "li42ee").unwrap(), + "li42ee".as_bytes()).unwrap(), Bencode::List(vec![ Bencode::Integer(42), ]) ); assert_eq!(Bencode::decode( - (format!("l{}e", test_encode_bytes("hallo"))).as_str()).unwrap(), + (format!("l{}e", test_encode_bytes("hallo"))).as_bytes()).unwrap(), Bencode::List(vec![ Bencode::Bytes(ByteString::from_str("hallo")), ]) ); assert_eq!(Bencode::decode( - (format!("l{}{}e", test_encode_bytes("hallo"), "i42e")).as_str()).unwrap(), + (format!("l{}{}e", test_encode_bytes("hallo"), "i42e")).as_bytes()).unwrap(), Bencode::List(vec![ Bencode::Bytes(ByteString::from_str("hallo")), Bencode::Integer(42), ]) ); assert_eq!(Bencode::decode( - (format!("l{}{}{}{}e", "i-17e", test_encode_bytes("hallo"), "i42e", test_encode_bytes("tschüssi💩"))).as_str()).unwrap(), + (format!("l{}{}{}{}e", "i-17e", test_encode_bytes("hallo"), "i42e", test_encode_bytes("tschüssi💩"))).as_bytes()).unwrap(), Bencode::List(vec![ Bencode::Integer(-17), Bencode::Bytes(ByteString::from_str("hallo")), @@ -337,14 +371,14 @@ mod tests { Bencode::Bytes(ByteString::from_str("tschüssi💩")), ]) ); - assert_eq!(Bencode::decode("llelee").unwrap(), + assert_eq!(Bencode::decode("llelee".as_bytes()).unwrap(), Bencode::List(vec![ Bencode::List(Vec::new()), Bencode::List(Vec::new()), ]), ); let str = format!("ll{}{}ee", test_encode_bytes("hallo"), "i42e"); - assert_eq!(Bencode::decode(&str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::List(vec![ Bencode::List(vec![ Bencode::Bytes(ByteString::from_str("hallo")), @@ -353,7 +387,7 @@ mod tests { ]), ); let str = format!("ll{}{}el{}{}{}ee", test_encode_bytes("hallo"), "i42e", "i17e", test_encode_bytes("tschüss💩"), "i33e"); - assert_eq!(Bencode::decode(&str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::List(vec![ Bencode::List(vec![ Bencode::Bytes(ByteString::from_str("hallo")), @@ -367,7 +401,7 @@ mod tests { ]), ); let str = format!("ll{}{}ed{}{}{}{}ee", test_encode_bytes("hallo"), "i42e", test_encode_bytes("foo"), "i23e", test_encode_bytes("bar"), test_encode_bytes("baz")); - assert_eq!(Bencode::decode(&str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::List(vec![ Bencode::List(vec![ Bencode::Bytes(ByteString::from_str("hallo")), @@ -421,36 +455,36 @@ mod tests { #[test] fn test_dict_str() { - assert_eq!(Bencode::decode("de").unwrap(), + assert_eq!(Bencode::decode("de".as_bytes()).unwrap(), Bencode::Dict(HashMap::new()) ); let str = "d3:fooi42ee"; - assert_eq!(Bencode::decode(str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("foo"), Bencode::Integer(42)), ])) ); let str = format!("d{}i42ee", test_encode_bytes("💩")); - assert_eq!(Bencode::decode(str.as_str()).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("💩"), Bencode::Integer(42)), ])) ); let str = "d3:foo3:bare"; - assert_eq!(Bencode::decode(str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))), ])) ); let str = "d3:fooi42e3:bar3:baze"; - assert_eq!(Bencode::decode(str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("foo"), Bencode::Integer(42)), (ByteString::from_str("bar"), Bencode::Bytes(ByteString::from_str("baz"))), ])) ); let str = "d3:fooli42ei17ee3:bar3:baze"; - assert_eq!(Bencode::decode(str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("foo"), Bencode::List( vec![Bencode::Integer(42), Bencode::Integer(17)])), @@ -458,7 +492,7 @@ mod tests { ])) ); let str = "d3:foo3:bare"; - assert_eq!(Bencode::decode(str).unwrap(), + assert_eq!(Bencode::decode(str.as_bytes()).unwrap(), Bencode::Dict(HashMap::from([ (ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))), ])) diff --git a/src/main.rs b/src/main.rs index 49def53..4f655ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,22 @@ +use std::{env, fs}; + +use anyhow::{anyhow, Result}; +use crate::bencode::Bencode; + mod bencode; -fn main() { - println!("Hello, world!"); +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + return Err(anyhow!("Please specify the torrent file to download")); + } + let file_path = &args[1]; + + let torrent_bytes = fs::read(&file_path)?; + + let _torrent_decoded = Bencode::decode(&torrent_bytes)?; + + println!("torrent decoded: {_torrent_decoded}"); + + Ok(()) }