Read actual torrent file and "pretty" print it
This commit is contained in:
parent
f9ed2388a2
commit
1dcf4f6a37
100
src/bencode.rs
100
src/bencode.rs
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use anyhow::{anyhow, ensure, Result};
|
use anyhow::{anyhow, ensure, Result};
|
||||||
|
|
||||||
@ -24,7 +25,19 @@ struct Torrent {
|
|||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct ByteString(Vec<u8>);
|
pub struct ByteString(Vec<u8>);
|
||||||
|
|
||||||
|
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 {
|
impl ByteString {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn from_str(input: &str) -> Self {
|
pub fn from_str(input: &str) -> Self {
|
||||||
ByteString(input.as_bytes().to_vec())
|
ByteString(input.as_bytes().to_vec())
|
||||||
}
|
}
|
||||||
@ -34,14 +47,6 @@ impl ByteString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum Bencode {
|
|
||||||
Integer(i64),
|
|
||||||
Bytes(ByteString),
|
|
||||||
List(Vec<Bencode>),
|
|
||||||
Dict(HashMap<ByteString, Bencode>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum BencodeType {
|
enum BencodeType {
|
||||||
Integer,
|
Integer,
|
||||||
@ -50,9 +55,38 @@ enum BencodeType {
|
|||||||
Dict,
|
Dict,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Bencode {
|
||||||
|
Integer(i64),
|
||||||
|
Bytes(ByteString),
|
||||||
|
List(Vec<Bencode>),
|
||||||
|
Dict(HashMap<ByteString, Bencode>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
impl Bencode {
|
||||||
pub fn decode(input: &str) -> Result<Bencode> {
|
pub fn decode(input: &[u8]) -> Result<Bencode> {
|
||||||
let (result, end_pos) = Self::decode_type(input.as_bytes())?;
|
let (result, end_pos) = Self::decode_type(input)?;
|
||||||
ensure!(end_pos == input.len() - 1,
|
ensure!(end_pos == input.len() - 1,
|
||||||
"Could not fully decode input. Got {} chars left to decode", input.len() - 1 - end_pos);
|
"Could not fully decode input. Got {} chars left to decode", input.len() - 1 - end_pos);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -188,9 +222,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_integer_str() {
|
fn test_integer_str() {
|
||||||
assert_eq!(Bencode::decode("i42e").unwrap(), Bencode::Integer(42));
|
assert_eq!(Bencode::decode("i42e".as_bytes()).unwrap(), Bencode::Integer(42));
|
||||||
assert_eq!(Bencode::decode("i17e").unwrap(), Bencode::Integer(17));
|
assert_eq!(Bencode::decode("i17e".as_bytes()).unwrap(), Bencode::Integer(17));
|
||||||
assert_eq!(Bencode::decode("i-17e").unwrap(), Bencode::Integer(-17));
|
assert_eq!(Bencode::decode("i-17e".as_bytes()).unwrap(), Bencode::Integer(-17));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -206,10 +240,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bytes_str() {
|
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("hallo").as_bytes()).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("tschüss").as_bytes()).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("💩").as_bytes()).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 💩, this is a long text").as_bytes()).unwrap(), Bencode::Bytes(ByteString::from_str("hallo 💩, this is a long text")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -306,30 +340,30 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_list_str() {
|
fn test_list_str() {
|
||||||
assert_eq!(Bencode::decode(
|
assert_eq!(Bencode::decode(
|
||||||
"le").unwrap(),
|
"le".as_bytes()).unwrap(),
|
||||||
Bencode::List(Vec::new())
|
Bencode::List(Vec::new())
|
||||||
);
|
);
|
||||||
assert_eq!(Bencode::decode(
|
assert_eq!(Bencode::decode(
|
||||||
"li42ee").unwrap(),
|
"li42ee".as_bytes()).unwrap(),
|
||||||
Bencode::List(vec![
|
Bencode::List(vec![
|
||||||
Bencode::Integer(42),
|
Bencode::Integer(42),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(Bencode::decode(
|
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::List(vec![
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
Bencode::Bytes(ByteString::from_str("hallo")),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(Bencode::decode(
|
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::List(vec![
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
Bencode::Bytes(ByteString::from_str("hallo")),
|
||||||
Bencode::Integer(42),
|
Bencode::Integer(42),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
assert_eq!(Bencode::decode(
|
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::List(vec![
|
||||||
Bencode::Integer(-17),
|
Bencode::Integer(-17),
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
Bencode::Bytes(ByteString::from_str("hallo")),
|
||||||
@ -337,14 +371,14 @@ mod tests {
|
|||||||
Bencode::Bytes(ByteString::from_str("tschüssi💩")),
|
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![
|
||||||
Bencode::List(Vec::new()),
|
Bencode::List(Vec::new()),
|
||||||
Bencode::List(Vec::new()),
|
Bencode::List(Vec::new()),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
let str = format!("ll{}{}ee", test_encode_bytes("hallo"), "i42e");
|
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::List(vec![
|
Bencode::List(vec![
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
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");
|
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::List(vec![
|
Bencode::List(vec![
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
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"));
|
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::List(vec![
|
Bencode::List(vec![
|
||||||
Bencode::Bytes(ByteString::from_str("hallo")),
|
Bencode::Bytes(ByteString::from_str("hallo")),
|
||||||
@ -421,36 +455,36 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dict_str() {
|
fn test_dict_str() {
|
||||||
assert_eq!(Bencode::decode("de").unwrap(),
|
assert_eq!(Bencode::decode("de".as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::new())
|
Bencode::Dict(HashMap::new())
|
||||||
);
|
);
|
||||||
let str = "d3:fooi42ee";
|
let str = "d3:fooi42ee";
|
||||||
assert_eq!(Bencode::decode(str).unwrap(),
|
assert_eq!(Bencode::decode(str.as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::from([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("foo"), Bencode::Integer(42)),
|
(ByteString::from_str("foo"), Bencode::Integer(42)),
|
||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
let str = format!("d{}i42ee", test_encode_bytes("💩"));
|
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([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("💩"), Bencode::Integer(42)),
|
(ByteString::from_str("💩"), Bencode::Integer(42)),
|
||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
let str = "d3:foo3:bare";
|
let str = "d3:foo3:bare";
|
||||||
assert_eq!(Bencode::decode(str).unwrap(),
|
assert_eq!(Bencode::decode(str.as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::from([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))),
|
(ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))),
|
||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
let str = "d3:fooi42e3:bar3:baze";
|
let str = "d3:fooi42e3:bar3:baze";
|
||||||
assert_eq!(Bencode::decode(str).unwrap(),
|
assert_eq!(Bencode::decode(str.as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::from([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("foo"), Bencode::Integer(42)),
|
(ByteString::from_str("foo"), Bencode::Integer(42)),
|
||||||
(ByteString::from_str("bar"), Bencode::Bytes(ByteString::from_str("baz"))),
|
(ByteString::from_str("bar"), Bencode::Bytes(ByteString::from_str("baz"))),
|
||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
let str = "d3:fooli42ei17ee3:bar3:baze";
|
let str = "d3:fooli42ei17ee3:bar3:baze";
|
||||||
assert_eq!(Bencode::decode(str).unwrap(),
|
assert_eq!(Bencode::decode(str.as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::from([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("foo"), Bencode::List(
|
(ByteString::from_str("foo"), Bencode::List(
|
||||||
vec![Bencode::Integer(42), Bencode::Integer(17)])),
|
vec![Bencode::Integer(42), Bencode::Integer(17)])),
|
||||||
@ -458,7 +492,7 @@ mod tests {
|
|||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
let str = "d3:foo3:bare";
|
let str = "d3:foo3:bare";
|
||||||
assert_eq!(Bencode::decode(str).unwrap(),
|
assert_eq!(Bencode::decode(str.as_bytes()).unwrap(),
|
||||||
Bencode::Dict(HashMap::from([
|
Bencode::Dict(HashMap::from([
|
||||||
(ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))),
|
(ByteString::from_str("foo"), Bencode::Bytes(ByteString::from_str("bar"))),
|
||||||
]))
|
]))
|
||||||
|
21
src/main.rs
21
src/main.rs
@ -1,5 +1,22 @@
|
|||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use crate::bencode::Bencode;
|
||||||
|
|
||||||
mod bencode;
|
mod bencode;
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
println!("Hello, world!");
|
let args: Vec<String> = 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(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user