1use core::str;
2use std::borrow::Cow;
3
4use anyhow::Context;
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct File<'a> {
10 pub length: u64,
12 #[serde(default, skip_serializing_if = "Option::is_none", borrow)]
14 pub md5sum: Option<Cow<'a, str>>,
15 #[serde(borrow)]
19 pub path: Vec<Cow<'a, str>>,
20}
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct Info<'a> {
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub files: Option<Vec<File<'a>>>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
29 pub length: Option<u64>,
30 #[serde(borrow)]
32 pub name: Cow<'a, str>,
33 #[serde(rename = "piece length")]
35 pub piece_length: usize,
36 #[serde(with = "serde_bytes", borrow)]
38 pub pieces: Cow<'a, [u8]>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub private: Option<u8>,
42 #[serde(default, skip_serializing_if = "Option::is_none", borrow)]
44 pub md5sum: Option<Cow<'a, str>>,
45}
46
47impl<'a> Info<'a> {
48 pub fn hash(&self) -> anyhow::Result<[u8; 20]> {
50 let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA1);
51 serde_bencode::to_writer(self, &mut hasher).context("serializing torrent's Info failed")?;
52 let mut resulting_hash = [0u8; 20];
53 resulting_hash.copy_from_slice(&hasher.finish());
54 Ok(resulting_hash)
55 }
56}
57
58#[derive(Debug, Deserialize)]
59pub struct MetaInfo<'a> {
60 #[serde(borrow)]
62 pub info: Info<'a>,
63 #[serde(borrow)]
65 pub announce: Cow<'a, str>,
66 #[serde(default, borrow)]
68 pub encoding: Option<Cow<'a, str>>,
69 #[serde(default)]
71 #[serde(rename = "announce-list", borrow)]
72 pub announce_list: Option<Vec<Vec<Cow<'a, str>>>>,
73 #[serde(default)]
75 #[serde(rename = "creation date")]
76 pub creation_date: Option<u64>,
77 #[serde(default, borrow)]
79 pub comment: Option<Cow<'a, str>>,
80 #[serde(default)]
82 #[serde(rename = "created by", borrow)]
83 pub created_by: Option<Cow<'a, str>>,
84}