Skip to content

Commit

Permalink
Peer metainfo
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Nov 7, 2023
1 parent e442d38 commit 788f603
Show file tree
Hide file tree
Showing 8 changed files with 390 additions and 129 deletions.
31 changes: 31 additions & 0 deletions src/extension.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
use anyhow::{Context, Error};

use crate::bencode::BencodeValue;

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Extension {
Metadata,
}

impl Extension {
pub fn id(&self) -> usize {
match self {
Extension::Metadata => 1,
}
}

pub fn name(&self) -> String {
match &self {
Extension::Metadata => "ut_metadata".into(),
Expand All @@ -28,3 +37,25 @@ impl Extension {
)
}
}

impl TryFrom<usize> for Extension {
type Error = Error;

fn try_from(value: usize) -> Result<Self, Self::Error> {
[Extension::Metadata]
.into_iter()
.find(|e| e.id() == value)
.context("unknown id")
}
}

impl TryFrom<&str> for Extension {
type Error = Error;

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"ut_metadata" => Ok(Extension::Metadata),
_ => Err(Error::msg("unknown extension")),
}
}
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod feature;
mod hex;
mod metainfo;
mod peer;
mod peer_metainfo;
mod persist;
mod sha1;
mod state;
Expand Down Expand Up @@ -48,7 +49,7 @@ async fn try_main() -> Result<()> {

let config = Config {
port: 6881,
respect_choke: true,
respect_choke: false,
choke_wait: Duration::from_secs(10),
reconnect_wait: Duration::from_secs(20),
downloaded_check_wait: Duration::from_secs(1),
Expand Down
36 changes: 19 additions & 17 deletions src/metainfo.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use core::fmt;
use std::path::PathBuf;

use anyhow::{Error, Result};

use crate::{bencode::BencodeValue, state::PieceHash};

#[derive(Clone, Debug, PartialEq, PartialOrd, Hash)]
pub struct Metainfo {
pub info: Info,
pub announce: String,
pub announce: Option<String>,
pub announce_list: Option<Vec<Vec<String>>>,
pub creation_date: Option<i64>,
pub comment: Option<String>,
Expand Down Expand Up @@ -64,32 +66,32 @@ pub struct PathInfo {
}

impl TryFrom<BencodeValue> for Metainfo {
type Error = String;
type Error = Error;

fn try_from(value: BencodeValue) -> Result<Self, Self::Error> {
let dict = match value {
BencodeValue::Dict(d) => d,
_ => return Err("metafile is not a dict".into()),
_ => return Err(Error::msg("metafile is not a dict")),
};
let info_dict = match dict.get("info") {
Some(BencodeValue::Dict(d)) => d,
_ => return Err("'info' is not a dict".into()),
_ => return Err(Error::msg("'info' is not a dict")),
};
let pieces: Vec<PieceHash> = match info_dict.get("pieces") {
Some(BencodeValue::String(s)) => s.chunks(20).map(|c| PieceHash(c.to_vec())).collect(),
_ => return Err("'pieces' missing".into()),
_ => return Err(Error::msg("'pieces' missing")),
};
let name: String = match info_dict.get("name") {
Some(BencodeValue::String(s)) => String::from_utf8_lossy(s).into(),
_ => return Err("'name' missing".into()),
_ => return Err(Error::msg("'name' missing")),
};
let file_info = match info_dict.get("files") {
Some(d) => FileInfo::Multi(parse_files_info(d)?),
None => FileInfo::Single(PathInfo {
path: PathBuf::from(&name),
length: match info_dict.get("length") {
Some(BencodeValue::Int(v)) => *v as u64,
_ => return Err("'length' missing".into()),
_ => return Err(Error::msg("'length' missing")),
},
md5_sum: match info_dict.get("md5_sum") {
Some(BencodeValue::String(s)) => Some(String::from_utf8_lossy(s).to_string()),
Expand All @@ -101,7 +103,7 @@ impl TryFrom<BencodeValue> for Metainfo {
info: Info {
piece_length: match info_dict.get("piece length") {
Some(BencodeValue::Int(v)) => *v as u64,
_ => return Err("'piece length' missing".into()),
_ => return Err(Error::msg("'piece length' missing")),
},
pieces,
name,
Expand All @@ -112,8 +114,8 @@ impl TryFrom<BencodeValue> for Metainfo {
},
},
announce: match dict.get("announce") {
Some(BencodeValue::String(s)) => String::from_utf8_lossy(s).into(),
_ => return Err("'announce' missing".into()),
Some(BencodeValue::String(s)) => Some(String::from_utf8_lossy(s).into()),
_ => None,
},
announce_list: match dict.get("announce-list") {
Some(BencodeValue::List(l)) => l
Expand Down Expand Up @@ -152,7 +154,7 @@ impl TryFrom<BencodeValue> for Metainfo {
}
}

fn parse_files_info(value: &BencodeValue) -> Result<Vec<PathInfo>, String> {
fn parse_files_info(value: &BencodeValue) -> Result<Vec<PathInfo>> {
match value {
BencodeValue::List(l) => l
.iter()
Expand All @@ -165,15 +167,15 @@ fn parse_files_info(value: &BencodeValue) -> Result<Vec<PathInfo>, String> {
BencodeValue::String(dir) => {
Ok(PathBuf::from(String::from_utf8_lossy(dir).to_string()))
}
_ => Err("'path' item is not a string".into()),
_ => Err(Error::msg("'path' item is not a string")),
})
.collect::<Result<PathBuf, String>>()?,
_ => return Err("'path' is not a list".into()),
.collect::<Result<_, _>>()?,
_ => return Err(Error::msg("'path' is not a list")),
};
Ok(PathInfo {
length: match d.get("length") {
Some(BencodeValue::Int(v)) => *v as u64,
_ => return Err("'length' missing".into()),
_ => return Err(Error::msg("'length' missing")),
},
path,
md5_sum: match d.get("md5_sum") {
Expand All @@ -184,9 +186,9 @@ fn parse_files_info(value: &BencodeValue) -> Result<Vec<PathInfo>, String> {
},
})
}
_ => Err("'files' item is not a dict".into()),
_ => Err(Error::msg("'files' item is not a dict")),
})
.collect(),
_ => Err("'files' is not a list".into()),
_ => Err(Error::msg("'files' is not a list")),
}
}
Loading

0 comments on commit 788f603

Please sign in to comment.