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 {
|
|
|
|
pub(crate) announce: 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"
|
|
|
|
)]
|
|
|
|
pub(crate) nodes: Option<Vec<Node>>,
|
2019-05-24 10:25:55 +02:00
|
|
|
}
|
2020-02-04 16:55:50 +01:00
|
|
|
|
|
|
|
impl Metainfo {
|
2020-02-11 12:08:57 +01:00
|
|
|
pub(crate) fn load(path: impl AsRef<Path>) -> Result<Metainfo, Error> {
|
2020-02-04 16:55:50 +01:00
|
|
|
let path = path.as_ref();
|
|
|
|
let bytes = fs::read(path).context(error::Filesystem { path })?;
|
|
|
|
Self::deserialize(path, &bytes)
|
|
|
|
}
|
|
|
|
|
2020-02-04 19:54:41 +01:00
|
|
|
#[cfg(test)]
|
2020-02-04 16:55:50 +01:00
|
|
|
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
|
|
|
let path = path.as_ref();
|
2020-02-14 09:12:49 +01:00
|
|
|
let bencode = bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
|
|
|
fs::write(path, &bencode).context(error::Filesystem { path })?;
|
2020-02-04 16:55:50 +01:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn deserialize(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Metainfo, Error> {
|
|
|
|
let path = path.as_ref();
|
2020-02-14 09:12:49 +01:00
|
|
|
bendy::serde::de::from_bytes(&bytes).context(error::MetainfoLoad { path })
|
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
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Metainfo {
|
2020-02-14 09:12:49 +01:00
|
|
|
bendy::serde::de::from_bytes(bytes).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn files<'a>(
|
|
|
|
&'a self,
|
|
|
|
base: &'a Path,
|
|
|
|
) -> Box<dyn Iterator<Item = (PathBuf, Bytes, Option<Md5Digest>)> + 'a> {
|
|
|
|
match &self.info.mode {
|
|
|
|
Mode::Single { length, md5sum } => Box::new(iter::once((base.to_owned(), *length, *md5sum))),
|
|
|
|
Mode::Multiple { files } => {
|
|
|
|
let base = base.to_owned();
|
|
|
|
Box::new(
|
|
|
|
files
|
|
|
|
.iter()
|
|
|
|
.map(move |file| (file.path.absolute(&base), file.length, file.md5sum)),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn verify(&self, base: &Path) -> Result<Status> {
|
|
|
|
Verifier::verify(self, base)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn round_trip_single() {
|
|
|
|
let value = Metainfo {
|
|
|
|
announce: "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()),
|
2020-02-14 11:16:19 +01:00
|
|
|
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
|
2020-02-14 09:12:49 +01:00
|
|
|
info: Info {
|
|
|
|
private: Some(true),
|
|
|
|
piece_length: Bytes(16 * 1024),
|
|
|
|
source: Some("source".into()),
|
|
|
|
name: "foo".into(),
|
2020-02-16 03:08:36 +01:00
|
|
|
pieces: PieceList::from_pieces(&["abc"]),
|
2020-02-14 09:12:49 +01:00
|
|
|
mode: Mode::Single {
|
|
|
|
length: Bytes(20),
|
|
|
|
md5sum: None,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
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 {
|
|
|
|
announce: "announce".into(),
|
|
|
|
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
|
2020-02-14 11:16:19 +01:00
|
|
|
nodes: Some(vec!["x:12".parse().unwrap(), "1.1.1.1:16".parse().unwrap()]),
|
2020-02-14 09:12:49 +01:00
|
|
|
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(),
|
2020-02-16 03:08:36 +01:00
|
|
|
pieces: PieceList::from_pieces(&["abc"]),
|
2020-02-14 09:12:49 +01:00
|
|
|
mode: Mode::Multiple {
|
|
|
|
files: vec![FileInfo {
|
|
|
|
length: Bytes(10),
|
|
|
|
path: FilePath::from_components(&["foo", "bar"]),
|
|
|
|
md5sum: Some(Md5Digest::from_hex("000102030405060708090a0b0c0d0e0f")),
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
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-04 16:55:50 +01:00
|
|
|
}
|