Metainfo refactoring

- Combine test Metainfo values into a small number of globally available
  values.
- Serialize update URLs as `URL` insted of `String`.
- Add additional `show` tests

type: reform
This commit is contained in:
Casey Rodarmor 2020-05-03 01:24:36 -07:00
parent f4f7a69069
commit 213624cf8e
No known key found for this signature in database
GPG Key ID: 556186B153EC6FE0
6 changed files with 510 additions and 522 deletions

View File

@ -39,7 +39,6 @@ strum_macros = "0.18.0"
syn = "1.0.14" syn = "1.0.14"
tempfile = "3.0.0" tempfile = "3.0.0"
unicode-width = "0.1.0" unicode-width = "0.1.0"
url = "2.1.1"
[dependencies.bendy] [dependencies.bendy]
version = "0.3.0" version = "0.3.0"
@ -57,6 +56,10 @@ features = ["derive"]
version = "0.3.0" version = "0.3.0"
features = ["default", "wrap_help"] features = ["default", "wrap_help"]
[dependencies.url]
version = "2.1.1"
features = ["serde"]
[dev-dependencies] [dev-dependencies]
claim = "0.3.1" claim = "0.3.1"
temptree = "0.0.0" temptree = "0.0.0"

View File

@ -26,7 +26,7 @@ pub(crate) struct Info {
with = "unwrap_or_skip", with = "unwrap_or_skip",
rename = "update-url" rename = "update-url"
)] )]
pub(crate) update_url: Option<String>, pub(crate) update_url: Option<Url>,
} }
impl Info { impl Info {

View File

@ -117,35 +117,138 @@ impl Metainfo {
pub(crate) fn infohash(&self) -> Result<Infohash> { pub(crate) fn infohash(&self) -> Result<Infohash> {
self.info.infohash() self.info.infohash()
} }
#[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()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("SOURCE".into()),
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50", "fae50"]),
mode: Mode::Single {
length: Bytes(32 * 1024),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
},
update_url: Some("https://update.example".parse().unwrap()),
},
}
}
#[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")),
}],
};
instance
}
#[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,
},
}
}
#[cfg(test)]
pub(crate) fn test_value_single_unset_infohash() -> &'static str {
"a9105b0ff5f7cefeee5599ed7831749be21cc04e"
}
#[cfg(test)]
pub(crate) fn test_value_single_unset_torrent_size() -> Bytes {
Bytes(85)
}
#[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,
info: Info {
private: None,
piece_length: Bytes(1024),
source: None,
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
mode: Mode::Multiple {
files: vec![FileInfo {
length: Bytes(1024),
md5sum: None,
path: FilePath::from_components(&["a", "b"]),
}],
},
update_url: None,
},
}
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use pretty_assertions::assert_eq;
#[test] #[test]
fn round_trip_single() { fn round_trip_single() {
let value = Metainfo { let value = Metainfo::test_value_single();
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
let bencode = bendy::serde::ser::to_bytes(&value).unwrap(); let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
@ -156,30 +259,7 @@ mod tests {
#[test] #[test]
fn round_trip_multiple() { fn round_trip_multiple() {
let value = Metainfo { let value = Metainfo::test_value_multiple();
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Multiple {
files: vec![FileInfo {
length: Bytes(10),
path: FilePath::from_components(&["foo", "bar"]),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
}],
},
update_url: None,
},
};
let bencode = bendy::serde::ser::to_bytes(&value).unwrap(); let bencode = bendy::serde::ser::to_bytes(&value).unwrap();
@ -192,8 +272,10 @@ mod tests {
let have = value.serialize().unwrap(); let have = value.serialize().unwrap();
if have != want.as_bytes() { if have != want.as_bytes() {
let have = String::from_utf8_lossy(&have);
assert_eq!(have, want);
eprintln!("have:"); eprintln!("have:");
eprintln!("{}", String::from_utf8_lossy(&have)); eprintln!("{}", have);
eprintln!("want:"); eprintln!("want:");
eprintln!("{}", want); eprintln!("{}", want);
panic!("Unexpected representation..."); panic!("Unexpected representation...");
@ -201,60 +283,40 @@ mod tests {
} }
#[test] #[test]
fn bencode_representation_single_some() { fn bencode_representation_single_set() {
let value = Metainfo { let value = Metainfo::test_value_single();
announce: Some("ANNOUNCE".into()),
announce_list: Some(vec![vec!["A".into(), "B".into()], vec!["C".into()]]),
nodes: Some(vec![
"domain:1".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[1234:5678:9abc:def0:1234:5678:9abc:def0]:65000"
.parse()
.unwrap(),
]),
comment: Some("COMMENT".into()),
created_by: Some("CREATED BY".into()),
creation_date: Some(0),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(1024),
source: Some("SOURCE".into()),
name: "NAME".into(),
pieces: PieceList::from_pieces(&["fae50"]),
mode: Mode::Single {
length: Bytes(5),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
},
update_url: None,
},
};
#[rustfmt::skip] #[rustfmt::skip]
let want = concat!( let want = concat!(
"d", "d",
"8:announce", "8:ANNOUNCE", "8:announce", "27:udp://announce.example:1337",
"13:announce-list", "l", "13:announce-list", "l",
"l", "1:A", "1:B", "e", "l",
"l", "1:C", "e", "21:http://a.example:4567",
"20:https://b.example:77",
"e",
"l",
"18:udp://c.example:88",
"e",
"e", "e",
"7:comment", "7:COMMENT", "7:comment", "7:COMMENT",
"10:created by", "10:CREATED BY", "10:created by", "10:CREATED BY",
"13:creation date", "i0e", "13:creation date", "i1e",
"8:encoding", "5:UTF-8", "8:encoding", "5:UTF-8",
"4:info", "d", "4:info", "d",
"6:length", "i5e", "6:length", "i32768e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f", "6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:name", "4:NAME", "4:name", "4:NAME",
"12:piece length", "i1024e", "12:piece length", "i16384e",
"6:pieces", "20:8,OS7d玤{Qk!Mk", "6:pieces", "40:8,OS7d玤{Qk!Mk8,OS7d玤{Qk!Mk",
"7:private", "i1e", "7:private", "i1e",
"6:source", "6:SOURCE", "6:source", "6:SOURCE",
"10:update-url", "23:https://update.example/",
"e", "e",
"5:nodes", "l", "5:nodes", "l",
"l", "6:domain", "i1e", "e", "l", "12:node.example", "i12e", "e",
"l", "7:1.1.1.1", "i16e", "e", "l", "7:1.1.1.1", "i16e", "e",
"l", "39:1234:5678:9abc:def0:1234:5678:9abc:def0", "i65000e", "e", "l", "23:2001:db8:85a3::8a2e:370", "i7334e", "e",
"e", "e",
"e" "e"
); );
@ -263,33 +325,12 @@ mod tests {
} }
#[test] #[test]
fn bencode_representation_single_none() { fn bencode_representation_single_unset() {
let value = Metainfo { let value = Metainfo::test_value_single_unset();
announce: Some("ANNOUNCE".into()),
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,
},
};
#[rustfmt::skip] #[rustfmt::skip]
let want = concat!( let want = concat!(
"d", "d",
"8:announce", "8:ANNOUNCE",
"4:info", "d", "4:info", "d",
"6:length", "i5e", "6:length", "i5e",
"4:name", "4:NAME", "4:name", "4:NAME",
@ -303,47 +344,45 @@ mod tests {
} }
#[test] #[test]
fn bencode_representation_multiple_some() { fn bencode_representation_multiple_set() {
let value = Metainfo { let value = Metainfo::test_value_multiple();
announce: Some("ANNOUNCE".into()),
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::Multiple {
files: vec![FileInfo {
length: Bytes(1024),
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
path: FilePath::from_components(&["a", "b"]),
}],
},
update_url: None,
},
};
#[rustfmt::skip] #[rustfmt::skip]
let want = concat!( let want = concat!(
"d", "d",
"8:announce", "8:ANNOUNCE", "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",
"4:info", "d", "4:info", "d",
"5:files", "l", "5:files", "l",
"d", "d",
"6:length", "i1024e", "6:length", "i32768e",
"6:md5sum", "32:000102030405060708090a0b0c0d0e0f", "6:md5sum", "32:000102030405060708090a0b0c0d0e0f",
"4:path", "l", "1:a", "1:b", "e", "4:path", "l", "3:DIR", "4:FILE", "e",
"e", "e",
"e", "e",
"4:name", "4:NAME", "4:name", "4:NAME",
"12:piece length", "i1024e", "12:piece length", "i16384e",
"6:pieces", "20:8,OS7d玤{Qk!Mk", "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",
"e", "e",
"e" "e"
); );
@ -352,36 +391,12 @@ mod tests {
} }
#[test] #[test]
fn bencode_representation_multiple_none() { fn bencode_representation_multiple_unset() {
let value = Metainfo { let value = Metainfo::test_value_multiple_unset();
announce: Some("ANNOUNCE".into()),
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::Multiple {
files: vec![FileInfo {
length: Bytes(1024),
md5sum: None,
path: FilePath::from_components(&["a", "b"]),
}],
},
update_url: None,
},
};
#[rustfmt::skip] #[rustfmt::skip]
let want = concat!( let want = concat!(
"d", "d",
"8:announce", "8:ANNOUNCE",
"4:info", "d", "4:info", "d",
"5:files", "l", "5:files", "l",
"d", "d",
@ -399,86 +414,45 @@ mod tests {
representation(value, want); representation(value, want);
} }
#[test]
fn private_false() {
let value = Metainfo {
announce: Some("ANNOUNCE".into()),
announce_list: None,
nodes: None,
comment: None,
created_by: None,
creation_date: None,
encoding: None,
info: Info {
private: Some(false),
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,
},
};
#[rustfmt::skip]
let want = concat!(
"d",
"8:announce", "8:ANNOUNCE",
"4:info", "d",
"6:length", "i5e",
"4:name", "4:NAME",
"12:piece length", "i1024e",
"6:pieces", "20:8,OS7d玤{Qk!Mk",
"7:private", "i0e",
"e",
"e"
);
representation(value, want);
}
#[test] #[test]
fn trackers() { fn trackers() {
let mut metainfo = Metainfo { fn assert_trackers_eq(metainfo: &Metainfo, want: &[&str]) {
announce: Some("http://foo".into()), let want = want
announce_list: None, .iter()
nodes: None, .cloned()
comment: None, .map(Url::parse)
created_by: None, .collect::<Result<Vec<Url>, url::ParseError>>()
creation_date: None, .unwrap();
encoding: None, let have = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap();
info: Info { assert_eq!(have, want);
private: Some(false), }
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,
},
};
let trackers = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap(); let mut metainfo = Metainfo::test_value_single();
assert_eq!(trackers, &["http://foo".parse().unwrap()]);
assert_trackers_eq(
&metainfo,
&[
"udp://announce.example:1337",
"http://a.example:4567",
"https://b.example:77",
"udp://c.example:88",
],
);
metainfo.announce_list = Some(vec![ metainfo.announce_list = Some(vec![
vec!["http://bar".into(), "http://baz".into()], vec![
vec!["http://foo".into()], "udp://announce.example:1337".into(),
"https://b.example:77".into(),
],
vec!["udp://c.example:88".into()],
]); ]);
let trackers = metainfo.trackers().collect::<Result<Vec<Url>>>().unwrap(); assert_trackers_eq(
assert_eq!( &metainfo,
trackers,
&[ &[
"http://foo".parse().unwrap(), "udp://announce.example:1337",
"http://bar".parse().unwrap(), "https://b.example:77",
"http://baz".parse().unwrap(), "udp://c.example:88",
], ],
); );
} }

View File

@ -382,13 +382,13 @@ impl Create {
} }
let info = Info { let info = Info {
source: self.source,
piece_length: content.piece_length,
name: content.name, name: content.name,
piece_length: content.piece_length,
source: self.source,
update_url: self.update_url,
mode, mode,
pieces, pieces,
private, private,
update_url: self.update_url.map(|url| url.to_string()),
}; };
let metainfo = Metainfo { let metainfo = Metainfo {
@ -3187,8 +3187,8 @@ Content Size 9 bytes
env.assert_ok(); env.assert_ok();
let metainfo = env.load_metainfo("foo.torrent"); let metainfo = env.load_metainfo("foo.torrent");
assert_eq!( assert_eq!(
metainfo.info.update_url.as_deref(), metainfo.info.update_url,
Some("https://www.a_real_url.com/") Some("https://www.a_real_url.com/".parse().unwrap())
); );
} }
} }

View File

@ -111,31 +111,53 @@ mod tests {
#[test] #[test]
fn output() -> Result<()> { fn output() -> Result<()> {
let metainfo = Metainfo { let metainfo = Metainfo::test_value_single();
announce: Some("announce".into()),
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]), #[rustfmt::skip]
nodes: Some(vec![ let want_human_readable = format!(
"x:12".parse().unwrap(), " Name NAME
"1.1.1.1:16".parse().unwrap(), Comment COMMENT
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(), Creation Date 1970-01-01 00:00:01 UTC
]), Created By CREATED BY
comment: Some("comment".into()), Source SOURCE
created_by: Some("created by".into()), Info Hash {}
creation_date: Some(1), Torrent Size {}
encoding: Some("UTF-8".into()), Content Size 32 KiB
info: Info { Private yes
private: Some(true), Tracker udp://announce.example:1337
piece_length: Bytes(16 * 1024), Announce List Tier 1: http://a.example:4567
source: Some("source".into()), https://b.example:77
name: "foo".into(), Tier 2: udp://c.example:88
pieces: PieceList::from_pieces(&["xyz", "abc"]), Update URL https://update.example/
mode: Mode::Single { DHT Nodes node.example:12
length: Bytes(20), 1.1.1.1:16
md5sum: None, [2001:db8:85a3::8a2e:370]:7334
}, Piece Size 16 KiB
update_url: None, Piece Count 2
}, File Count 1
}; Files NAME
", Metainfo::test_value_single_infohash(), Metainfo::test_value_single_torrent_size());
#[rustfmt::skip]
let want_machine_readable = format!("\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC
created by\tCREATED BY
source\tSOURCE
info hash\t{}
torrent size\t{}
content size\t32768
private\tyes
tracker\tudp://announce.example:1337
announce list\thttp://a.example:4567\thttps://b.example:77\tudp://c.example:88
update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tNAME
", Metainfo::test_value_single_infohash(), Metainfo::test_value_single_torrent_size().count());
{ {
let mut env = TestEnvBuilder::new() let mut env = TestEnvBuilder::new()
@ -150,29 +172,7 @@ mod tests {
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = " Name foo assert_eq!(have, want_human_readable);
Comment comment
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5
Torrent Size 339 bytes
Content Size 20 bytes
Private yes
Tracker announce
Announce List Tier 1: announce
b
Tier 2: c
DHT Nodes x:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files foo
";
assert_eq!(have, want);
} }
{ {
@ -188,29 +188,8 @@ Announce List Tier 1: announce
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = " Name foo
Comment comment
Creation Date 1970-01-01 00:00:01 UTC
Created By created by
Source source
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5
Torrent Size 339 bytes
Content Size 20 bytes
Private yes
Tracker announce
Announce List Tier 1: announce
b
Tier 2: c
DHT Nodes x:12
1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB
Piece Count 2
File Count 1
Files foo
";
assert_eq!(have, want); assert_eq!(have, want_human_readable);
} }
{ {
@ -225,26 +204,8 @@ Announce List Tier 1: announce
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = "\
name\tfoo
comment\tcomment
creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by
source\tsource
info hash\te12253978dc6d50db11d05747abcea1ad03b51c5
torrent size\t339
content size\t20
private\tyes
tracker\tannounce
announce list\tannounce\tb\tc
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384
piece count\t2
file count\t1
files\tfoo
";
assert_eq!(have, want); assert_eq!(have, want_machine_readable);
} }
Ok(()) Ok(())
@ -252,31 +213,7 @@ files\tfoo
#[test] #[test]
fn tier_list_with_main() -> Result<()> { fn tier_list_with_main() -> Result<()> {
let metainfo = Metainfo { let metainfo = Metainfo::test_value_single();
announce: Some("a".into()),
announce_list: Some(vec![vec!["x".into()], vec!["y".into()], vec!["z".into()]]),
comment: Some("comment".into()),
created_by: Some("created by".into()),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::0000:8a2e:0370]:7334".parse().unwrap(),
]),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["xyz", "abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
{ {
let mut env = TestEnvBuilder::new() let mut env = TestEnvBuilder::new()
@ -291,27 +228,34 @@ files\tfoo
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = " Name foo
Comment comment #[rustfmt::skip]
let want = format!(
" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC Creation Date 1970-01-01 00:00:01 UTC
Created By created by Created By CREATED BY
Source source Source SOURCE
Info Hash e12253978dc6d50db11d05747abcea1ad03b51c5 Info Hash {}
Torrent Size 327 bytes Torrent Size {}
Content Size 20 bytes Content Size 32 KiB
Private yes Private yes
Tracker a Tracker udp://announce.example:1337
Announce List Tier 1: x Announce List Tier 1: http://a.example:4567
Tier 2: y https://b.example:77
Tier 3: z Tier 2: udp://c.example:88
DHT Nodes x:12 Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16 1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334 [2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB Piece Size 16 KiB
Piece Count 2 Piece Count 2
File Count 1 File Count 1
Files foo Files NAME
"; ",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size()
);
assert_eq!(have, want); assert_eq!(have, want);
} }
@ -328,24 +272,31 @@ Announce List Tier 1: x
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = "\
name\tfoo #[rustfmt::skip]
comment\tcomment let want = format!(
"\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by created by\tCREATED BY
source\tsource source\tSOURCE
info hash\te12253978dc6d50db11d05747abcea1ad03b51c5 info hash\t{}
torrent size\t327 torrent size\t{}
content size\t20 content size\t32768
private\tyes private\tyes
tracker\ta tracker\tudp://announce.example:1337
announce list\tx\ty\tz announce list\thttp://a.example:4567\thttps://b.example:77\tudp://c.example:88
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334 update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384 piece size\t16384
piece count\t2 piece count\t2
file count\t1 file count\t1
files\tfoo files\tNAME
"; ",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count()
);
assert_eq!(have, want); assert_eq!(have, want);
} }
@ -355,31 +306,13 @@ files\tfoo
#[test] #[test]
fn tier_list_without_main() -> Result<()> { fn tier_list_without_main() -> Result<()> {
let metainfo = Metainfo { let mut metainfo = Metainfo::test_value_single();
announce: Some("a".into()),
announce_list: Some(vec![vec!["b".into()], vec!["c".into()], vec!["a".into()]]), metainfo.announce_list = Some(vec![
comment: Some("comment".into()), vec!["B".into()],
nodes: Some(vec![ vec!["C".into()],
"x:12".parse().unwrap(), vec!["ANNOUNCE".into()],
"1.1.1.1:16".parse().unwrap(), ]);
"[2001:0db8:85a3::8a2e:0370]:7334".parse().unwrap(),
]),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
{ {
let mut env = TestEnvBuilder::new() let mut env = TestEnvBuilder::new()
@ -394,27 +327,34 @@ files\tfoo
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = " Name foo
Comment comment #[rustfmt::skip]
let want = format!(
" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC Creation Date 1970-01-01 00:00:01 UTC
Created By created by Created By CREATED BY
Source source Source SOURCE
Info Hash b9cd9cae5748518c99d00d8ae86c0162510be4d9 Info Hash {}
Torrent Size 307 bytes Torrent Size {}
Content Size 20 bytes Content Size 32 KiB
Private yes Private yes
Tracker a Tracker udp://announce.example:1337
Announce List Tier 1: b Announce List Tier 1: B
Tier 2: c Tier 2: C
Tier 3: a Tier 3: ANNOUNCE
DHT Nodes x:12 Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16 1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334 [2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB Piece Size 16 KiB
Piece Count 1 Piece Count 2
File Count 1 File Count 1
Files foo Files NAME
"; ",
Metainfo::test_value_single_infohash(),
Bytes(Metainfo::test_value_single_torrent_size().count() - 50)
);
assert_eq!(have, want); assert_eq!(have, want);
} }
@ -431,24 +371,30 @@ Announce List Tier 1: b
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = "\
name\tfoo #[rustfmt::skip]
comment\tcomment let want = format!("\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by created by\tCREATED BY
source\tsource source\tSOURCE
info hash\tb9cd9cae5748518c99d00d8ae86c0162510be4d9 info hash\t{}
torrent size\t307 torrent size\t{}
content size\t20 content size\t32768
private\tyes private\tyes
tracker\ta tracker\tudp://announce.example:1337
announce list\tb\tc\ta announce list\tB\tC\tANNOUNCE
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334 update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384 piece size\t16384
piece count\t1 piece count\t2
file count\t1 file count\t1
files\tfoo files\tNAME
"; ",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count() - 50
);
assert_eq!(have, want); assert_eq!(have, want);
} }
@ -458,31 +404,9 @@ files\tfoo
#[test] #[test]
fn trackerless() -> Result<()> { fn trackerless() -> Result<()> {
let metainfo = Metainfo { let mut metainfo = Metainfo::test_value_single();
announce: None, metainfo.announce = None;
announce_list: None, metainfo.announce_list = None;
comment: Some("comment".into()),
nodes: Some(vec![
"x:12".parse().unwrap(),
"1.1.1.1:16".parse().unwrap(),
"[2001:0db8:85a3::8a2e:0370]:7334".parse().unwrap(),
]),
created_by: Some("created by".into()),
creation_date: Some(1),
encoding: Some("UTF-8".into()),
info: Info {
private: Some(true),
piece_length: Bytes(16 * 1024),
source: Some("source".into()),
name: "foo".into(),
pieces: PieceList::from_pieces(&["abc"]),
mode: Mode::Single {
length: Bytes(20),
md5sum: None,
},
update_url: None,
},
};
{ {
let mut env = TestEnvBuilder::new() let mut env = TestEnvBuilder::new()
@ -497,23 +421,29 @@ files\tfoo
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = " Name foo
Comment comment #[rustfmt::skip]
let want = format!(" Name NAME
Comment COMMENT
Creation Date 1970-01-01 00:00:01 UTC Creation Date 1970-01-01 00:00:01 UTC
Created By created by Created By CREATED BY
Source source Source SOURCE
Info Hash b9cd9cae5748518c99d00d8ae86c0162510be4d9 Info Hash {}
Torrent Size 261 bytes Torrent Size {}
Content Size 20 bytes Content Size 32 KiB
Private yes Private yes
DHT Nodes x:12 Update URL https://update.example/
DHT Nodes node.example:12
1.1.1.1:16 1.1.1.1:16
[2001:db8:85a3::8a2e:370]:7334 [2001:db8:85a3::8a2e:370]:7334
Piece Size 16 KiB Piece Size 16 KiB
Piece Count 1 Piece Count 2
File Count 1 File Count 1
Files foo Files NAME
"; ",
Metainfo::test_value_single_infohash(),
Bytes(Metainfo::test_value_single_torrent_size().count() - 130)
);
assert_eq!(have, want); assert_eq!(have, want);
} }
@ -530,22 +460,99 @@ Creation Date 1970-01-01 00:00:01 UTC
env.assert_ok(); env.assert_ok();
let have = env.out(); let have = env.out();
let want = "\ #[rustfmt::skip]
name\tfoo let want = format!(
comment\tcomment "\
name\tNAME
comment\tCOMMENT
creation date\t1970-01-01 00:00:01 UTC creation date\t1970-01-01 00:00:01 UTC
created by\tcreated by created by\tCREATED BY
source\tsource source\tSOURCE
info hash\tb9cd9cae5748518c99d00d8ae86c0162510be4d9 info hash\t{}
torrent size\t261 torrent size\t{}
content size\t20 content size\t32768
private\tyes private\tyes
dht nodes\tx:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334 update url\thttps://update.example/
dht nodes\tnode.example:12\t1.1.1.1:16\t[2001:db8:85a3::8a2e:370]:7334
piece size\t16384 piece size\t16384
piece count\t2
file count\t1
files\tNAME
",
Metainfo::test_value_single_infohash(),
Metainfo::test_value_single_torrent_size().count() - 130
);
assert_eq!(have, want);
}
Ok(())
}
#[test]
fn unset() -> Result<()> {
let metainfo = Metainfo::test_value_single_unset();
{
let mut env = TestEnvBuilder::new()
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
.out_is_term()
.build();
let path = env.resolve("foo.torrent")?;
metainfo.dump(path).unwrap();
env.assert_ok();
let have = env.out();
#[rustfmt::skip]
let want = format!(" Name NAME
Info Hash {}
Torrent Size {}
Content Size 5 bytes
Private no
Piece Size 1 KiB
Piece Count 1
File Count 1
Files NAME
",
Metainfo::test_value_single_unset_infohash(),
Metainfo::test_value_single_unset_torrent_size()
);
assert_eq!(have, want);
}
{
let mut env = TestEnvBuilder::new()
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
.build();
let path = env.resolve("foo.torrent")?;
metainfo.dump(path).unwrap();
env.assert_ok();
let have = env.out();
#[rustfmt::skip]
let want = format!(
"\
name\tNAME
info hash\t{}
torrent size\t{}
content size\t5
private\tno
piece size\t1024
piece count\t1 piece count\t1
file count\t1 file count\t1
files\tfoo files\tNAME
"; ",
Metainfo::test_value_single_unset_infohash(),
Metainfo::test_value_single_unset_torrent_size().count()
);
assert_eq!(have, want); assert_eq!(have, want);
} }

View File

@ -105,6 +105,10 @@ impl TorrentSummary {
table.tiers("Announce List", value); table.tiers("Announce List", value);
} }
if let Some(update_url) = &self.metainfo.info.update_url {
table.row("Update URL", update_url);
}
if let Some(nodes) = &self.metainfo.nodes { if let Some(nodes) = &self.metainfo.nodes {
table.list( table.list(
"DHT Nodes", "DHT Nodes",