2019-05-24 10:25:55 +02:00
|
|
|
|
use crate::common::*;
|
|
|
|
|
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) struct Metainfo {
|
2020-03-17 11:02:02 +01:00
|
|
|
|
#[serde(
|
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
|
|
|
|
pub(crate) announce: Option<String>,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[serde(
|
2020-02-14 11:16:19 +01:00
|
|
|
|
rename = "announce-list",
|
2020-02-14 09:12:49 +01:00
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) announce_list: Option<Vec<Vec<String>>>,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[serde(
|
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) comment: Option<String>,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[serde(
|
2020-02-14 11:16:19 +01:00
|
|
|
|
rename = "created by",
|
2020-02-14 09:12:49 +01:00
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) created_by: Option<String>,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[serde(
|
2020-02-14 11:16:19 +01:00
|
|
|
|
rename = "creation date",
|
2020-02-14 09:12:49 +01:00
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) creation_date: Option<u64>,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
#[serde(
|
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-02-04 17:51:56 +01:00
|
|
|
|
pub(crate) encoding: Option<String>,
|
|
|
|
|
pub(crate) info: Info,
|
2020-02-14 11:16:19 +01:00
|
|
|
|
#[serde(
|
|
|
|
|
skip_serializing_if = "Option::is_none",
|
|
|
|
|
default,
|
|
|
|
|
with = "unwrap_or_skip"
|
|
|
|
|
)]
|
2020-03-17 11:02:02 +01:00
|
|
|
|
pub(crate) nodes: Option<Vec<HostPort>>,
|
2019-05-24 10:25:55 +02:00
|
|
|
|
}
|
2020-02-04 16:55:50 +01:00
|
|
|
|
|
|
|
|
|
impl Metainfo {
|
2020-03-18 10:48:57 +01:00
|
|
|
|
pub(crate) fn from_input(input: &Input) -> Result<Metainfo> {
|
|
|
|
|
Self::deserialize(input.source(), input.data())
|
2020-02-04 16:55:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-18 10:48:57 +01:00
|
|
|
|
pub(crate) fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> {
|
|
|
|
|
let metainfo = bendy::serde::de::from_bytes(&data).context(error::MetainfoDeserialize {
|
|
|
|
|
input: source.clone(),
|
|
|
|
|
})?;
|
2020-03-06 06:44:20 +01:00
|
|
|
|
Ok(metainfo)
|
2020-02-04 16:55:50 +01:00
|
|
|
|
}
|
2020-02-04 17:36:00 +01:00
|
|
|
|
|
|
|
|
|
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
2020-02-14 09:12:49 +01:00
|
|
|
|
bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
2020-02-11 12:08:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 06:44:20 +01:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
|
|
|
|
let path = path.as_ref();
|
|
|
|
|
let bencode = bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
|
|
|
|
fs::write(path, &bencode).context(error::Filesystem { path })?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-11 12:08:57 +01:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Metainfo {
|
2020-04-03 05:04:12 +02:00
|
|
|
|
Self::deserialize(&InputTarget::Path("<TEST>".into()), bytes).unwrap()
|
2020-02-14 09:12:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 02:08:55 +01:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn file_paths(&self) -> Vec<String> {
|
|
|
|
|
let files = match &self.info.mode {
|
|
|
|
|
Mode::Single { .. } => panic!(),
|
|
|
|
|
Mode::Multiple { files } => files,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let paths: Vec<String> = files.iter().map(|f| f.path.to_string()).collect();
|
|
|
|
|
|
|
|
|
|
paths
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 06:05:49 +01:00
|
|
|
|
pub(crate) fn verify(&self, base: &Path, progress_bar: Option<ProgressBar>) -> Result<Status> {
|
|
|
|
|
Verifier::verify(self, base, progress_bar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn content_size(&self) -> Bytes {
|
|
|
|
|
self.info.content_size()
|
2020-02-14 09:12:49 +01:00
|
|
|
|
}
|
2020-03-17 11:02:02 +01:00
|
|
|
|
|
|
|
|
|
pub(crate) fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a {
|
2020-03-18 08:34:37 +01:00
|
|
|
|
let mut seen = HashSet::new();
|
2020-03-17 11:02:02 +01:00
|
|
|
|
iter::once(&self.announce)
|
|
|
|
|
.flatten()
|
|
|
|
|
.chain(self.announce_list.iter().flatten().flatten())
|
2020-03-18 08:34:37 +01:00
|
|
|
|
.filter_map(move |text| {
|
|
|
|
|
if seen.contains(text) {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
seen.insert(text.clone());
|
|
|
|
|
Some(text.parse().context(error::AnnounceUrlParse))
|
|
|
|
|
}
|
|
|
|
|
})
|
2020-03-17 11:02:02 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-07 05:01:55 +02:00
|
|
|
|
/// See `Info::infohash_lossy` for details on when this function is lossy.
|
|
|
|
|
pub(crate) fn infohash_lossy(&self) -> Result<Infohash> {
|
|
|
|
|
self.info.infohash_lossy()
|
2020-03-17 11:02:02 +01:00
|
|
|
|
}
|
2020-02-14 09:12:49 +01:00
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single() -> Metainfo {
|
|
|
|
|
Metainfo {
|
|
|
|
|
announce: Some("udp://announce.example:1337".into()),
|
|
|
|
|
announce_list: Some(vec![
|
|
|
|
|
vec![
|
|
|
|
|
"http://a.example:4567".into(),
|
|
|
|
|
"https://b.example:77".into(),
|
|
|
|
|
],
|
|
|
|
|
vec!["udp://c.example:88".into()],
|
|
|
|
|
]),
|
|
|
|
|
nodes: Some(vec![
|
|
|
|
|
"node.example:12".parse().unwrap(),
|
|
|
|
|
"1.1.1.1:16".parse().unwrap(),
|
|
|
|
|
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(),
|
|
|
|
|
]),
|
|
|
|
|
comment: Some("COMMENT".into()),
|
|
|
|
|
created_by: Some("CREATED BY".into()),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
creation_date: Some(1),
|
|
|
|
|
encoding: Some("UTF-8".into()),
|
|
|
|
|
info: Info {
|
|
|
|
|
private: Some(true),
|
|
|
|
|
piece_length: Bytes(16 * 1024),
|
2020-05-03 10:24:36 +02:00
|
|
|
|
source: Some("SOURCE".into()),
|
|
|
|
|
name: "NAME".into(),
|
|
|
|
|
pieces: PieceList::from_pieces(&["fae50", "fae50"]),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
mode: Mode::Single {
|
2020-05-03 10:24:36 +02:00
|
|
|
|
length: Bytes(32 * 1024),
|
|
|
|
|
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
},
|
2020-05-03 10:24:36 +02:00
|
|
|
|
update_url: Some("https://update.example".parse().unwrap()),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
},
|
2020-05-03 10:24:36 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single_infohash() -> &'static str {
|
|
|
|
|
"5d6f53772b4c20536fcce0c4c364d764a6efa39c"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single_torrent_size() -> Bytes {
|
|
|
|
|
Bytes(509)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_multiple() -> Metainfo {
|
|
|
|
|
let mut instance = Self::test_value_single();
|
|
|
|
|
instance.info.mode = Mode::Multiple {
|
|
|
|
|
files: vec![FileInfo {
|
|
|
|
|
length: Bytes(32 * 1024),
|
|
|
|
|
path: FilePath::from_components(&["DIR", "FILE"]),
|
|
|
|
|
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
|
|
|
|
|
}],
|
2020-02-14 09:12:49 +01:00
|
|
|
|
};
|
2020-05-03 10:24:36 +02:00
|
|
|
|
instance
|
|
|
|
|
}
|
2020-02-14 09:12:49 +01:00
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single_unset() -> Metainfo {
|
|
|
|
|
Metainfo {
|
|
|
|
|
announce: None,
|
|
|
|
|
announce_list: None,
|
|
|
|
|
nodes: None,
|
|
|
|
|
comment: None,
|
|
|
|
|
created_by: None,
|
|
|
|
|
creation_date: None,
|
|
|
|
|
encoding: None,
|
|
|
|
|
info: Info {
|
|
|
|
|
private: None,
|
|
|
|
|
piece_length: Bytes(1024),
|
|
|
|
|
source: None,
|
|
|
|
|
name: "NAME".into(),
|
|
|
|
|
pieces: PieceList::from_pieces(&["fae50"]),
|
|
|
|
|
mode: Mode::Single {
|
|
|
|
|
length: Bytes(5),
|
|
|
|
|
md5sum: None,
|
|
|
|
|
},
|
|
|
|
|
update_url: None,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-14 09:12:49 +01:00
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single_unset_infohash() -> &'static str {
|
|
|
|
|
"a9105b0ff5f7cefeee5599ed7831749be21cc04e"
|
|
|
|
|
}
|
2020-02-14 09:12:49 +01:00
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_single_unset_torrent_size() -> Bytes {
|
|
|
|
|
Bytes(85)
|
2020-02-14 09:12:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub(crate) fn test_value_multiple_unset() -> Metainfo {
|
|
|
|
|
Metainfo {
|
|
|
|
|
announce: None,
|
|
|
|
|
announce_list: None,
|
|
|
|
|
nodes: None,
|
|
|
|
|
comment: None,
|
|
|
|
|
created_by: None,
|
|
|
|
|
creation_date: None,
|
|
|
|
|
encoding: None,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
info: Info {
|
2020-05-03 10:24:36 +02:00
|
|
|
|
private: None,
|
|
|
|
|
piece_length: Bytes(1024),
|
|
|
|
|
source: None,
|
|
|
|
|
name: "NAME".into(),
|
|
|
|
|
pieces: PieceList::from_pieces(&["fae50"]),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
mode: Mode::Multiple {
|
|
|
|
|
files: vec![FileInfo {
|
2020-05-03 10:24:36 +02:00
|
|
|
|
length: Bytes(1024),
|
|
|
|
|
md5sum: None,
|
|
|
|
|
path: FilePath::from_components(&["a", "b"]),
|
2020-02-14 09:12:49 +01:00
|
|
|
|
}],
|
|
|
|
|
},
|
2020-04-28 02:03:58 +02:00
|
|
|
|
update_url: None,
|
2020-02-14 09:12:49 +01:00
|
|
|
|
},
|
2020-05-03 10:24:36 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn round_trip_single() {
|
|
|
|
|
let value = Metainfo::test_value_single();
|
|
|
|
|
|
|
|
|
|
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
|
|
|
|
|
|
|
|
|
|
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(value, deserialized);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn round_trip_multiple() {
|
|
|
|
|
let value = Metainfo::test_value_multiple();
|
2020-02-14 09:12:49 +01:00
|
|
|
|
|
|
|
|
|
let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
|
|
|
|
|
|
|
|
|
|
let deserialized = bendy::serde::de::from_bytes(&bencode).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(value, deserialized);
|
2020-02-04 17:36:00 +01:00
|
|
|
|
}
|
2020-02-19 14:34:03 +01:00
|
|
|
|
|
|
|
|
|
fn representation(value: Metainfo, want: &str) {
|
|
|
|
|
let have = value.serialize().unwrap();
|
|
|
|
|
|
|
|
|
|
if have != want.as_bytes() {
|
2020-05-03 10:24:36 +02:00
|
|
|
|
let have = String::from_utf8_lossy(&have);
|
|
|
|
|
assert_eq!(have, want);
|
2020-02-19 14:34:03 +01:00
|
|
|
|
eprintln!("have:");
|
2020-05-03 10:24:36 +02:00
|
|
|
|
eprintln!("{}", have);
|
2020-02-19 14:34:03 +01:00
|
|
|
|
eprintln!("want:");
|
|
|
|
|
eprintln!("{}", want);
|
|
|
|
|
panic!("Unexpected representation...");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-05-03 10:24:36 +02:00
|
|
|
|
fn bencode_representation_single_set() {
|
|
|
|
|
let value = Metainfo::test_value_single();
|
2020-02-19 14:34:03 +01:00
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
let want = concat!(
|
|
|
|
|
"d",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"8:announce", "27:udp://announce.example:1337",
|
|
|
|
|
"13:announce-list", "l",
|
|
|
|
|
"l",
|
|
|
|
|
"21:http://a.example:4567",
|
|
|
|
|
"20:https://b.example:77",
|
|
|
|
|
"e",
|
|
|
|
|
"l",
|
|
|
|
|
"18:udp://c.example:88",
|
|
|
|
|
"e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"e",
|
|
|
|
|
"7:comment", "7:COMMENT",
|
|
|
|
|
"10:created by", "10:CREATED BY",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"13:creation date", "i1e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"8:encoding", "5:UTF-8",
|
|
|
|
|
"4:info", "d",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"6:length", "i32768e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
|
|
|
|
|
"4:name", "4:NAME",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"12:piece length", "i16384e",
|
|
|
|
|
"6:pieces", "40:8,OS7d玤{Qk!Mk8,OS7d玤{Qk!Mk",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"7:private", "i1e",
|
|
|
|
|
"6:source", "6:SOURCE",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"10:update-url", "23:https://update.example/",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"e",
|
|
|
|
|
"5:nodes", "l",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"l", "12:node.example", "i12e", "e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"l", "7:1.1.1.1", "i16e", "e",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"e",
|
|
|
|
|
"e"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
representation(value, want);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-05-03 10:24:36 +02:00
|
|
|
|
fn bencode_representation_single_unset() {
|
|
|
|
|
let value = Metainfo::test_value_single_unset();
|
2020-02-19 14:34:03 +01:00
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
let want = concat!(
|
|
|
|
|
"d",
|
|
|
|
|
"4:info", "d",
|
|
|
|
|
"6:length", "i5e",
|
|
|
|
|
"4:name", "4:NAME",
|
|
|
|
|
"12:piece length", "i1024e",
|
|
|
|
|
"6:pieces", "20:8,OS7d玤{Qk!Mk",
|
|
|
|
|
"e",
|
|
|
|
|
"e"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
representation(value, want);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-05-03 10:24:36 +02:00
|
|
|
|
fn bencode_representation_multiple_set() {
|
|
|
|
|
let value = Metainfo::test_value_multiple();
|
2020-02-19 14:34:03 +01:00
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
let want = concat!(
|
|
|
|
|
"d",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"8:announce", "27:udp://announce.example:1337",
|
|
|
|
|
"13:announce-list", "l",
|
|
|
|
|
"l",
|
|
|
|
|
"21:http://a.example:4567",
|
|
|
|
|
"20:https://b.example:77",
|
|
|
|
|
"e",
|
|
|
|
|
"l",
|
|
|
|
|
"18:udp://c.example:88",
|
|
|
|
|
"e",
|
|
|
|
|
"e",
|
|
|
|
|
"7:comment", "7:COMMENT",
|
|
|
|
|
"10:created by", "10:CREATED BY",
|
|
|
|
|
"13:creation date", "i1e",
|
|
|
|
|
"8:encoding", "5:UTF-8",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"4:info", "d",
|
|
|
|
|
"5:files", "l",
|
|
|
|
|
"d",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"6:length", "i32768e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"4:path", "l", "3:DIR", "4:FILE", "e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"e",
|
|
|
|
|
"e",
|
|
|
|
|
"4:name", "4:NAME",
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"12:piece length", "i16384e",
|
|
|
|
|
"6:pieces", "40:8,OS7d玤{Qk!Mk8,OS7d玤{Qk!Mk",
|
|
|
|
|
"7:private", "i1e",
|
|
|
|
|
"6:source", "6:SOURCE",
|
|
|
|
|
"10:update-url", "23:https://update.example/",
|
|
|
|
|
"e",
|
|
|
|
|
"5:nodes", "l",
|
|
|
|
|
"l", "12:node.example", "i12e", "e",
|
|
|
|
|
"l", "7:1.1.1.1", "i16e", "e",
|
|
|
|
|
"l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e",
|
2020-02-19 14:34:03 +01:00
|
|
|
|
"e",
|
|
|
|
|
"e"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
representation(value, want);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2020-05-03 10:24:36 +02:00
|
|
|
|
fn bencode_representation_multiple_unset() {
|
|
|
|
|
let value = Metainfo::test_value_multiple_unset();
|
2020-02-19 14:34:03 +01:00
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
let want = concat!(
|
|
|
|
|
"d",
|
|
|
|
|
"4:info", "d",
|
|
|
|
|
"5:files", "l",
|
|
|
|
|
"d",
|
|
|
|
|
"6:length", "i1024e",
|
|
|
|
|
"4:path", "l", "1:a", "1:b", "e",
|
|
|
|
|
"e",
|
|
|
|
|
"e",
|
|
|
|
|
"4:name", "4:NAME",
|
|
|
|
|
"12:piece length", "i1024e",
|
|
|
|
|
"6:pieces", "20:8,OS7d玤{Qk!Mk",
|
|
|
|
|
"e",
|
|
|
|
|
"e"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
representation(value, want);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-18 08:34:37 +01:00
|
|
|
|
#[test]
|
|
|
|
|
fn trackers() {
|
2020-05-03 10:24:36 +02:00
|
|
|
|
fn assert_trackers_eq(metainfo: &Metainfo, want: &[&str]) {
|
|
|
|
|
let want = want
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.map(Url::parse)
|
|
|
|
|
.collect::<Result<Vec<Url>, url::ParseError>>()
|
|
|
|
|
.unwrap();
|
|
|
|
|
let have = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap();
|
|
|
|
|
assert_eq!(have, want);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut metainfo = Metainfo::test_value_single();
|
2020-03-18 08:34:37 +01:00
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
assert_trackers_eq(
|
|
|
|
|
&metainfo,
|
|
|
|
|
&[
|
|
|
|
|
"udp://announce.example:1337",
|
|
|
|
|
"http://a.example:4567",
|
|
|
|
|
"https://b.example:77",
|
|
|
|
|
"udp://c.example:88",
|
|
|
|
|
],
|
|
|
|
|
);
|
2020-03-18 08:34:37 +01:00
|
|
|
|
|
|
|
|
|
metainfo.announce_list = Some(vec![
|
2020-05-03 10:24:36 +02:00
|
|
|
|
vec![
|
|
|
|
|
"udp://announce.example:1337".into(),
|
|
|
|
|
"https://b.example:77".into(),
|
|
|
|
|
],
|
|
|
|
|
vec!["udp://c.example:88".into()],
|
2020-03-18 08:34:37 +01:00
|
|
|
|
]);
|
|
|
|
|
|
2020-05-03 10:24:36 +02:00
|
|
|
|
assert_trackers_eq(
|
|
|
|
|
&metainfo,
|
2020-03-18 08:34:37 +01:00
|
|
|
|
&[
|
2020-05-03 10:24:36 +02:00
|
|
|
|
"udp://announce.example:1337",
|
|
|
|
|
"https://b.example:77",
|
|
|
|
|
"udp://c.example:88",
|
2020-03-18 08:34:37 +01:00
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-02-04 16:55:50 +01:00
|
|
|
|
}
|