Compare commits
5 Commits
2346c30fec
...
ea3b22fccc
Author | SHA1 | Date | |
---|---|---|---|
ea3b22fccc | |||
|
984543fcb9 | ||
|
6c4805890b | ||
|
b82ccf1882 | ||
|
379a001f47 |
13
.github/workflows/build.yaml
vendored
13
.github/workflows/build.yaml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: "-D warnings"
|
RUSTFLAGS: --deny warnings
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -63,14 +63,9 @@ jobs:
|
||||||
target
|
target
|
||||||
key: cargo-${{ env.CACHE_KEY }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
|
key: cargo-${{ env.CACHE_KEY }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Install Stable
|
- name: Install Rust Toolchain Components
|
||||||
uses: actions-rs/toolchain@v1
|
run: |
|
||||||
with:
|
rustup component add clippy rustfmt
|
||||||
toolchain: stable
|
|
||||||
target: ${{matrix.target}}
|
|
||||||
profile: minimal
|
|
||||||
components: clippy, rustfmt
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Info
|
- name: Info
|
||||||
run: |
|
run: |
|
||||||
|
|
657
Cargo.lock
generated
657
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
bin/package
15
bin/package
|
@ -26,16 +26,13 @@ case $os in
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $os in
|
RUSTFLAGS='--deny warnings --codegen target-feature=+crt-static' \
|
||||||
ubuntu-latest | macos-latest)
|
cargo build --bin $bin --target $target --release
|
||||||
cargo rustc --bin $bin --target $target --release
|
|
||||||
executable=target/$target/release/$bin
|
executable=target/$target/release/$bin
|
||||||
;;
|
|
||||||
windows-latest)
|
if [[ $os == windows-2016 ]]; then
|
||||||
cargo rustc --bin $bin --target $target --release -- -C target-feature="+crt-static"
|
executable=$executable.exe
|
||||||
executable=target/$target/release/$bin.exe
|
fi
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "Building completions..."
|
echo "Building completions..."
|
||||||
cargo run --package gen -- --bin $executable completion-scripts
|
cargo run --package gen -- --bin $executable completion-scripts
|
||||||
|
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1.51.0
|
|
@ -7,9 +7,9 @@ const TI: u64 = GI << 10;
|
||||||
const PI: u64 = TI << 10;
|
const PI: u64 = TI << 10;
|
||||||
const EI: u64 = PI << 10;
|
const EI: u64 = PI << 10;
|
||||||
|
|
||||||
#[serde(transparent)]
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)]
|
||||||
pub(crate) struct Bytes(pub(crate) u64);
|
#[serde(transparent)]
|
||||||
|
pub struct Bytes(pub(crate) u64);
|
||||||
|
|
||||||
impl Bytes {
|
impl Bytes {
|
||||||
pub(crate) fn kib() -> Self {
|
pub(crate) fn kib() -> Self {
|
||||||
|
@ -89,7 +89,7 @@ impl FromStr for Bytes {
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::ByteSuffix {
|
return Err(Error::ByteSuffix {
|
||||||
text: text.to_owned(),
|
text: text.to_owned(),
|
||||||
suffix: suffix.to_owned(),
|
suffix: suffix.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct Env {
|
pub struct Env {
|
||||||
args: Vec<OsString>,
|
args: Vec<OsString>,
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
input: Box<dyn InputStream>,
|
input: Box<dyn InputStream>,
|
||||||
|
@ -41,7 +41,7 @@ impl Env {
|
||||||
.and_then(|width| width.parse::<usize>().ok());
|
.and_then(|width| width.parse::<usize>().ok());
|
||||||
|
|
||||||
if let Some(width) = width {
|
if let Some(width) = width {
|
||||||
app = app.set_term_width(width)
|
app = app.set_term_width(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
app
|
app
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
#[snafu(visibility(pub(crate)))]
|
#[snafu(visibility(pub(crate)))]
|
||||||
pub(crate) enum Error {
|
pub enum Error {
|
||||||
#[snafu(display("Failed to parse announce URL: {}", source))]
|
#[snafu(display("Failed to parse announce URL: {}", source))]
|
||||||
AnnounceUrlParse { source: url::ParseError },
|
AnnounceUrlParse { source: url::ParseError },
|
||||||
#[snafu(display("Failed to parse byte count `{}`: {}", text, source))]
|
#[snafu(display("Failed to parse byte count `{}`: {}", text, source))]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[serde(transparent)]
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub(crate) struct FilePath {
|
pub(crate) struct FilePath {
|
||||||
components: Vec<String>,
|
components: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub(crate) struct HostPort {
|
pub struct HostPort {
|
||||||
host: Host,
|
host: Host,
|
||||||
port: u16,
|
port: u16,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
#[snafu(visibility(pub(crate)))]
|
#[snafu(visibility(pub(crate)))]
|
||||||
pub(crate) enum HostPortParseError {
|
pub enum HostPortParseError {
|
||||||
#[snafu(display("Failed to parse host `{}`: {}", text, source))]
|
#[snafu(display("Failed to parse host `{}`: {}", text, source))]
|
||||||
Host {
|
Host {
|
||||||
text: String,
|
text: String,
|
||||||
|
|
12
src/info.rs
12
src/info.rs
|
@ -1,22 +1,22 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
pub(crate) struct Info {
|
pub struct Info {
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) private: Option<bool>,
|
pub private: Option<bool>,
|
||||||
#[serde(rename = "piece length")]
|
#[serde(rename = "piece length")]
|
||||||
pub(crate) piece_length: Bytes,
|
pub piece_length: Bytes,
|
||||||
pub(crate) name: String,
|
pub name: String,
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) source: Option<String>,
|
pub source: Option<String>,
|
||||||
pub(crate) pieces: PieceList,
|
pub(crate) pieces: PieceList,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(crate) mode: Mode,
|
pub(crate) mode: Mode,
|
||||||
|
@ -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<Url>,
|
pub update_url: Option<Url>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Info {
|
impl Info {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub(crate) struct Infohash {
|
pub struct Infohash {
|
||||||
inner: Sha1Digest,
|
pub inner: Sha1Digest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Infohash {
|
impl Infohash {
|
||||||
pub(crate) fn from_input(input: &Input) -> Result<Infohash, Error> {
|
pub fn from_input(input: &Input) -> Result<Infohash, Error> {
|
||||||
let value = Value::from_bencode(input.data()).map_err(|error| Error::MetainfoDecode {
|
let value = Value::from_bencode(input.data()).map_err(|error| Error::MetainfoDecode {
|
||||||
input: input.source().clone(),
|
input: input.source().clone(),
|
||||||
error,
|
error,
|
||||||
|
@ -43,7 +43,7 @@ impl Infohash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_bencoded_info_dict(info: &[u8]) -> Infohash {
|
pub fn from_bencoded_info_dict(info: &[u8]) -> Infohash {
|
||||||
Infohash {
|
Infohash {
|
||||||
inner: Sha1Digest::from_data(info),
|
inner: Sha1Digest::from_data(info),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct Input {
|
pub struct Input {
|
||||||
source: InputTarget,
|
source: InputTarget,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub(crate) fn new(source: InputTarget, data: Vec<u8>) -> Input {
|
pub fn new(source: InputTarget, data: Vec<u8>) -> Input {
|
||||||
Self { source, data }
|
Self { source, data }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn data(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn source(&self) -> &InputTarget {
|
pub fn source(&self) -> &InputTarget {
|
||||||
&self.source
|
&self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub(crate) enum InputTarget {
|
pub enum InputTarget {
|
||||||
Path(PathBuf),
|
Path(PathBuf),
|
||||||
Stdin,
|
Stdin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputTarget {
|
impl InputTarget {
|
||||||
pub(crate) fn try_from_os_str(text: &OsStr) -> Result<Self, OsString> {
|
pub fn try_from_os_str(text: &OsStr) -> Result<Self, OsString> {
|
||||||
text
|
text
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|err: Error| OsString::from(err.to_string()))
|
.map_err(|err: Error| OsString::from(err.to_string()))
|
||||||
|
|
126
src/lib.rs
126
src/lib.rs
|
@ -41,85 +41,85 @@
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod assert_matches;
|
pub mod assert_matches;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod errln;
|
pub mod errln;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod err;
|
pub mod err;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod out;
|
pub mod out;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod outln;
|
pub mod outln;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod test_env;
|
pub mod test_env;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_env_builder;
|
pub mod test_env_builder;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod capture;
|
pub mod capture;
|
||||||
|
|
||||||
mod arguments;
|
pub mod arguments;
|
||||||
mod bytes;
|
pub mod bytes;
|
||||||
mod common;
|
pub mod common;
|
||||||
mod consts;
|
pub mod consts;
|
||||||
mod env;
|
pub mod env;
|
||||||
mod error;
|
pub mod error;
|
||||||
mod file_error;
|
pub mod file_error;
|
||||||
mod file_info;
|
pub mod file_info;
|
||||||
mod file_path;
|
pub mod file_path;
|
||||||
mod file_status;
|
pub mod file_status;
|
||||||
mod files;
|
pub mod files;
|
||||||
mod hasher;
|
pub mod hasher;
|
||||||
mod host_port;
|
pub mod host_port;
|
||||||
mod host_port_parse_error;
|
pub mod host_port_parse_error;
|
||||||
mod info;
|
pub mod info;
|
||||||
mod infohash;
|
pub mod infohash;
|
||||||
mod input;
|
pub mod input;
|
||||||
mod input_stream;
|
pub mod input_stream;
|
||||||
mod input_target;
|
pub mod input_target;
|
||||||
mod into_u64;
|
pub mod into_u64;
|
||||||
mod into_usize;
|
pub mod into_usize;
|
||||||
mod invariant;
|
pub mod invariant;
|
||||||
mod lint;
|
pub mod lint;
|
||||||
mod linter;
|
pub mod linter;
|
||||||
mod magnet_link;
|
pub mod magnet_link;
|
||||||
mod magnet_link_parse_error;
|
pub mod magnet_link_parse_error;
|
||||||
mod md5_digest;
|
pub mod md5_digest;
|
||||||
mod metainfo;
|
pub mod metainfo;
|
||||||
mod metainfo_error;
|
pub mod metainfo_error;
|
||||||
mod mode;
|
pub mod mode;
|
||||||
mod options;
|
pub mod options;
|
||||||
mod output_stream;
|
pub mod output_stream;
|
||||||
mod output_target;
|
pub mod output_target;
|
||||||
mod piece_length_picker;
|
pub mod piece_length_picker;
|
||||||
mod piece_list;
|
pub mod piece_list;
|
||||||
mod platform;
|
pub mod platform;
|
||||||
mod platform_interface;
|
pub mod platform_interface;
|
||||||
mod print;
|
pub mod print;
|
||||||
mod reckoner;
|
pub mod reckoner;
|
||||||
mod run;
|
pub mod run;
|
||||||
mod sha1_digest;
|
pub mod sha1_digest;
|
||||||
mod shell;
|
pub mod shell;
|
||||||
mod sort_key;
|
pub mod sort_key;
|
||||||
mod sort_order;
|
pub mod sort_order;
|
||||||
mod sort_spec;
|
pub mod sort_spec;
|
||||||
mod status;
|
pub mod status;
|
||||||
mod step;
|
pub mod step;
|
||||||
mod style;
|
pub mod style;
|
||||||
mod subcommand;
|
pub mod subcommand;
|
||||||
mod table;
|
pub mod table;
|
||||||
mod torrent_summary;
|
pub mod torrent_summary;
|
||||||
mod use_color;
|
pub mod use_color;
|
||||||
mod verifier;
|
pub mod verifier;
|
||||||
mod walker;
|
pub mod walker;
|
||||||
mod xor_args;
|
pub mod xor_args;
|
||||||
|
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
pub mod bench;
|
pub mod bench;
|
||||||
|
|
|
@ -12,7 +12,7 @@ impl Linter {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn allow(&mut self, allowed: impl IntoIterator<Item = Lint>) {
|
pub(crate) fn allow(&mut self, allowed: impl IntoIterator<Item = Lint>) {
|
||||||
self.allowed.extend(allowed)
|
self.allowed.extend(allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_allowed(&self, lint: Lint) -> bool {
|
pub(crate) fn is_allowed(&self, lint: Lint) -> bool {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) struct MagnetLink {
|
pub struct MagnetLink {
|
||||||
infohash: Infohash,
|
pub infohash: Infohash,
|
||||||
name: Option<String>,
|
pub name: Option<String>,
|
||||||
peers: Vec<HostPort>,
|
pub peers: Vec<HostPort>,
|
||||||
trackers: Vec<Url>,
|
pub trackers: Vec<Url>,
|
||||||
indices: BTreeSet<u64>,
|
pub indices: BTreeSet<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MagnetLink {
|
impl MagnetLink {
|
||||||
/// See `Info::infohash_lossy` for details on when this function is lossy.
|
/// See `Info::infohash_lossy` for details on when this function is lossy.
|
||||||
pub(crate) fn from_metainfo_lossy(metainfo: &Metainfo) -> Result<MagnetLink> {
|
pub fn from_metainfo_lossy(metainfo: &Metainfo) -> Result<MagnetLink> {
|
||||||
let mut link = Self::with_infohash(metainfo.infohash_lossy()?);
|
let mut link = Self::with_infohash(metainfo.infohash_lossy()?);
|
||||||
|
|
||||||
link.set_name(metainfo.info.name.clone());
|
link.set_name(metainfo.info.name.clone());
|
||||||
|
@ -23,7 +23,7 @@ impl MagnetLink {
|
||||||
Ok(link)
|
Ok(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with_infohash(infohash: Infohash) -> Self {
|
pub fn with_infohash(infohash: Infohash) -> Self {
|
||||||
MagnetLink {
|
MagnetLink {
|
||||||
infohash,
|
infohash,
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -34,23 +34,23 @@ impl MagnetLink {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn set_name(&mut self, name: impl Into<String>) {
|
pub fn set_name(&mut self, name: impl Into<String>) {
|
||||||
self.name = Some(name.into());
|
self.name = Some(name.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_peer(&mut self, peer: HostPort) {
|
pub fn add_peer(&mut self, peer: HostPort) {
|
||||||
self.peers.push(peer);
|
self.peers.push(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_tracker(&mut self, tracker: Url) {
|
pub fn add_tracker(&mut self, tracker: Url) {
|
||||||
self.trackers.push(tracker);
|
self.trackers.push(tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_index(&mut self, index: u64) {
|
pub fn add_index(&mut self, index: u64) {
|
||||||
self.indices.insert(index);
|
self.indices.insert(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn to_url(&self) -> Url {
|
pub fn to_url(&self) -> Url {
|
||||||
let mut url = Url::parse("magnet:").invariant_unwrap("`magnet:` is valid URL");
|
let mut url = Url::parse("magnet:").invariant_unwrap("`magnet:` is valid URL");
|
||||||
|
|
||||||
let mut query = format!("xt=urn:btih:{}", self.infohash);
|
let mut query = format!("xt=urn:btih:{}", self.infohash);
|
||||||
|
@ -85,7 +85,7 @@ impl MagnetLink {
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(text: &str) -> Result<Self, MagnetLinkParseError> {
|
pub fn parse(text: &str) -> Result<Self, MagnetLinkParseError> {
|
||||||
let url = Url::parse(&text).context(magnet_link_parse_error::Url)?;
|
let url = Url::parse(&text).context(magnet_link_parse_error::Url)?;
|
||||||
|
|
||||||
if url.scheme() != "magnet" {
|
if url.scheme() != "magnet" {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
#[snafu(visibility(pub(crate)))]
|
#[snafu(visibility(pub(crate)))]
|
||||||
pub(crate) enum MagnetLinkParseError {
|
pub enum MagnetLinkParseError {
|
||||||
#[snafu(display("Failed to parse hex string `{}`: {}", text, source))]
|
#[snafu(display("Failed to parse hex string `{}`: {}", text, source))]
|
||||||
HexParse {
|
HexParse {
|
||||||
text: String,
|
text: String,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[serde(transparent)]
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub(crate) struct Md5Digest {
|
pub(crate) struct Md5Digest {
|
||||||
#[serde(with = "SerHex::<serde_hex::Strict>")]
|
#[serde(with = "SerHex::<serde_hex::Strict>")]
|
||||||
bytes: [u8; 16],
|
bytes: [u8; 16],
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
pub(crate) struct Metainfo {
|
pub struct Metainfo {
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) announce: Option<String>,
|
pub announce: Option<String>,
|
||||||
#[serde(
|
#[serde(
|
||||||
rename = "announce-list",
|
rename = "announce-list",
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) announce_list: Option<Vec<Vec<String>>>,
|
pub announce_list: Option<Vec<Vec<String>>>,
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
#[serde(
|
#[serde(
|
||||||
rename = "created by",
|
rename = "created by",
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) created_by: Option<String>,
|
pub created_by: Option<String>,
|
||||||
#[serde(
|
#[serde(
|
||||||
rename = "creation date",
|
rename = "creation date",
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) creation_date: Option<u64>,
|
pub creation_date: Option<u64>,
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) encoding: Option<String>,
|
pub encoding: Option<String>,
|
||||||
pub(crate) info: Info,
|
pub info: Info,
|
||||||
#[serde(
|
#[serde(
|
||||||
skip_serializing_if = "Option::is_none",
|
skip_serializing_if = "Option::is_none",
|
||||||
default,
|
default,
|
||||||
with = "unwrap_or_skip"
|
with = "unwrap_or_skip"
|
||||||
)]
|
)]
|
||||||
pub(crate) nodes: Option<Vec<HostPort>>,
|
pub nodes: Option<Vec<HostPort>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metainfo {
|
impl Metainfo {
|
||||||
pub(crate) fn from_input(input: &Input) -> Result<Metainfo> {
|
pub fn from_input(input: &Input) -> Result<Metainfo> {
|
||||||
Self::deserialize(input.source(), input.data())
|
Self::deserialize(input.source(), input.data())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> {
|
pub fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> {
|
||||||
let metainfo = bendy::serde::de::from_bytes(&data).context(error::MetainfoDeserialize {
|
let metainfo = bendy::serde::de::from_bytes(&data).context(error::MetainfoDeserialize {
|
||||||
input: source.clone(),
|
input: source.clone(),
|
||||||
})?;
|
})?;
|
||||||
Ok(metainfo)
|
Ok(metainfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> {
|
pub fn serialize(&self) -> Result<Vec<u8>, Error> {
|
||||||
bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ impl Metainfo {
|
||||||
self.info.content_size()
|
self.info.content_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a {
|
pub fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a {
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
iter::once(&self.announce)
|
iter::once(&self.announce)
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub(crate) enum MetainfoError {
|
pub enum MetainfoError {
|
||||||
Type,
|
Type,
|
||||||
InfoMissing,
|
InfoMissing,
|
||||||
InfoType,
|
InfoType,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub(crate) enum Mode {
|
pub(crate) enum Mode {
|
||||||
Single {
|
Single {
|
||||||
length: Bytes,
|
length: Bytes,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct OutputStream {
|
pub(crate) struct OutputStream {
|
||||||
|
active: bool,
|
||||||
stream: Box<dyn Write>,
|
stream: Box<dyn Write>,
|
||||||
style: bool,
|
style: bool,
|
||||||
term: bool,
|
term: bool,
|
||||||
active: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputStream {
|
impl OutputStream {
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||||
pub(crate) struct Sha1Digest {
|
pub struct Sha1Digest {
|
||||||
bytes: [u8; Self::LENGTH],
|
pub bytes: [u8; Self::LENGTH],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sha1Digest {
|
impl Sha1Digest {
|
||||||
pub(crate) const LENGTH: usize = 20;
|
pub const LENGTH: usize = 20;
|
||||||
|
|
||||||
pub(crate) fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self {
|
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self {
|
||||||
Sha1Digest { bytes }
|
Sha1Digest { bytes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn bytes(self) -> [u8; Self::LENGTH] {
|
pub fn bytes(self) -> [u8; Self::LENGTH] {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_data(data: impl AsRef<[u8]>) -> Self {
|
pub fn from_data(data: impl AsRef<[u8]>) -> Self {
|
||||||
Sha1::from(data).digest().into()
|
Sha1::from(data).digest().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use structopt::clap;
|
||||||
|
|
||||||
#[derive(Copy, Clone, EnumVariantNames, IntoStaticStr, EnumString, EnumIter, Debug)]
|
#[derive(Copy, Clone, EnumVariantNames, IntoStaticStr, EnumString, EnumIter, Debug)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub(crate) enum Shell {
|
pub enum Shell {
|
||||||
Zsh,
|
Zsh,
|
||||||
Bash,
|
Bash,
|
||||||
Fish,
|
Fish,
|
||||||
|
@ -62,6 +62,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variants() {
|
fn variants() {
|
||||||
assert_eq!(Shell::VARIANTS, clap::Shell::variants())
|
assert_eq!(Shell::VARIANTS, clap::Shell::variants());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,9 +338,7 @@ impl Create {
|
||||||
|
|
||||||
if let OutputTarget::Path(path) = &output {
|
if let OutputTarget::Path(path) = &output {
|
||||||
if !self.force && path.exists() {
|
if !self.force && path.exists() {
|
||||||
return Err(Error::OutputExists {
|
return Err(Error::OutputExists { path: path.clone() });
|
||||||
path: path.to_owned(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,7 +1149,7 @@ mod tests {
|
||||||
length: Bytes(3),
|
length: Bytes(3),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1182,7 +1180,7 @@ mod tests {
|
||||||
length: Bytes(4),
|
length: Bytes(4),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1213,7 +1211,7 @@ mod tests {
|
||||||
length: Bytes(4),
|
length: Bytes(4),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1258,7 +1256,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1285,7 +1283,7 @@ mod tests {
|
||||||
length: Bytes(3),
|
length: Bytes(3),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1319,7 +1317,7 @@ mod tests {
|
||||||
length: Bytes(3),
|
length: Bytes(3),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1346,7 +1344,7 @@ mod tests {
|
||||||
length: Bytes(0),
|
length: Bytes(0),
|
||||||
md5sum: None,
|
md5sum: None,
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1367,7 +1365,7 @@ mod tests {
|
||||||
env.assert_ok();
|
env.assert_ok();
|
||||||
let metainfo = env.load_metainfo("foo.torrent");
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
assert_eq!(metainfo.info.pieces.count(), 0);
|
assert_eq!(metainfo.info.pieces.count(), 0);
|
||||||
assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() })
|
assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -16,7 +16,19 @@ const INPUT_FLAG: &str = "input-flag";
|
||||||
version_message(consts::VERSION_MESSAGE),
|
version_message(consts::VERSION_MESSAGE),
|
||||||
about("Verify files against a .torrent file.")
|
about("Verify files against a .torrent file.")
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(test, structopt(setting = AppSettings::ColorNever))]
|
||||||
pub(crate) struct Verify {
|
pub(crate) struct Verify {
|
||||||
|
#[structopt(
|
||||||
|
long = "base-directory",
|
||||||
|
short = "b",
|
||||||
|
value_name = "BASE-DIRECTORY",
|
||||||
|
conflicts_with = "content",
|
||||||
|
empty_values(false),
|
||||||
|
parse(from_os_str),
|
||||||
|
help = "Look for torrent content in `BASE-DIRECTORY`/`NAME`, where `NAME` is the `name` field \
|
||||||
|
of the torrent info dictionary."
|
||||||
|
)]
|
||||||
|
base_directory: Option<PathBuf>,
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long = "content",
|
long = "content",
|
||||||
short = "c",
|
short = "c",
|
||||||
|
@ -24,7 +36,7 @@ pub(crate) struct Verify {
|
||||||
empty_values(false),
|
empty_values(false),
|
||||||
parse(from_os_str),
|
parse(from_os_str),
|
||||||
help = "Verify torrent content at `PATH` against torrent metainfo. Defaults to `name` field \
|
help = "Verify torrent content at `PATH` against torrent metainfo. Defaults to `name` field \
|
||||||
of torrent info dictionary."
|
of the torrent info dictionary."
|
||||||
)]
|
)]
|
||||||
content: Option<PathBuf>,
|
content: Option<PathBuf>,
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -64,13 +76,19 @@ impl Verify {
|
||||||
|
|
||||||
let metainfo = Metainfo::from_input(&input)?;
|
let metainfo = Metainfo::from_input(&input)?;
|
||||||
|
|
||||||
let content = self.content.as_ref().map_or_else(
|
let content = self
|
||||||
|| match target {
|
.content
|
||||||
|
.as_ref()
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| {
|
||||||
|
self
|
||||||
|
.base_directory
|
||||||
|
.map(|base_directory| base_directory.join(&metainfo.info.name).lexiclean())
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| match target {
|
||||||
InputTarget::Path(path) => path.join("..").join(&metainfo.info.name).lexiclean(),
|
InputTarget::Path(path) => path.join("..").join(&metainfo.info.name).lexiclean(),
|
||||||
InputTarget::Stdin => PathBuf::from(&metainfo.info.name),
|
InputTarget::Stdin => PathBuf::from(&metainfo.info.name),
|
||||||
},
|
});
|
||||||
PathBuf::clone,
|
|
||||||
);
|
|
||||||
|
|
||||||
let progress_bar = if env.err().is_styled_term() && !options.quiet {
|
let progress_bar = if env.err().is_styled_term() && !options.quiet {
|
||||||
let style = ProgressStyle::default_bar()
|
let style = ProgressStyle::default_bar()
|
||||||
|
@ -137,6 +155,20 @@ mod tests {
|
||||||
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn base_directory_conflicts_with_content() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: ["torrent", "verify", "foo.torrent", "--content", "foo", "--base-directory", "dir"],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
env.run().unwrap_err().to_string(),
|
||||||
|
"error: The argument '--content <PATH>' cannot be used with '--base-directory \
|
||||||
|
<BASE-DIRECTORY>'\n\nUSAGE:\n imdl torrent verify <INPUT> --base-directory \
|
||||||
|
<BASE-DIRECTORY> --content <PATH>\n\nFor more information try --help\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pass() -> Result<()> {
|
fn pass() -> Result<()> {
|
||||||
let mut create_env = test_env! {
|
let mut create_env = test_env! {
|
||||||
|
@ -323,6 +355,62 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn base_directory() -> Result<()> {
|
||||||
|
let mut create_env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"https://bar",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
a: "abc",
|
||||||
|
d: "efg",
|
||||||
|
h: "ijk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
create_env.assert_ok();
|
||||||
|
|
||||||
|
create_env.create_dir("dir");
|
||||||
|
|
||||||
|
create_env.rename("foo", "dir/foo");
|
||||||
|
|
||||||
|
let torrent = create_env.resolve("foo.torrent")?;
|
||||||
|
let dir = create_env.resolve("dir")?;
|
||||||
|
|
||||||
|
let mut verify_env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"verify",
|
||||||
|
"--input",
|
||||||
|
&torrent,
|
||||||
|
"--base-directory",
|
||||||
|
&dir,
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
verify_env.assert_ok();
|
||||||
|
|
||||||
|
let want = format!(
|
||||||
|
"[1/2] \u{1F4BE} Loading metainfo from `{}`…\n[2/2] \u{1F9EE} Verifying pieces from \
|
||||||
|
`{}`…\n\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}\n",
|
||||||
|
torrent.display(),
|
||||||
|
dir.join("foo").display(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(verify_env.err(), want);
|
||||||
|
assert_eq!(verify_env.out(), "");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_stdin() -> Result<()> {
|
fn verify_stdin() -> Result<()> {
|
||||||
let mut create_env = test_env! {
|
let mut create_env = test_env! {
|
||||||
|
|
|
@ -37,19 +37,19 @@ macro_rules! test_env {
|
||||||
|
|
||||||
pub(crate) struct TestEnv {
|
pub(crate) struct TestEnv {
|
||||||
env: Env,
|
env: Env,
|
||||||
#[allow(unused)]
|
|
||||||
tempdir: TempDir,
|
|
||||||
err: Capture,
|
err: Capture,
|
||||||
out: Capture,
|
out: Capture,
|
||||||
|
#[allow(unused)]
|
||||||
|
tempdir: TempDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestEnv {
|
impl TestEnv {
|
||||||
pub(crate) fn new(tempdir: TempDir, env: Env, err: Capture, out: Capture) -> TestEnv {
|
pub(crate) fn new(tempdir: TempDir, env: Env, err: Capture, out: Capture) -> TestEnv {
|
||||||
Self {
|
Self {
|
||||||
tempdir,
|
|
||||||
err,
|
|
||||||
env,
|
env,
|
||||||
|
err,
|
||||||
out,
|
out,
|
||||||
|
tempdir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,14 @@ impl TestEnv {
|
||||||
fs::create_dir(self.env.resolve(path).unwrap()).unwrap();
|
fs::create_dir(self.env.resolve(path).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn rename(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) {
|
||||||
|
fs::rename(
|
||||||
|
self.env.resolve(from).unwrap(),
|
||||||
|
self.env.resolve(to).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn read_to_string(&self, path: impl AsRef<Path>) -> String {
|
pub(crate) fn read_to_string(&self, path: impl AsRef<Path>) -> String {
|
||||||
fs::read_to_string(self.env.resolve(path).unwrap()).unwrap()
|
fs::read_to_string(self.env.resolve(path).unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct TorrentSummary {
|
pub struct TorrentSummary {
|
||||||
infohash: Infohash,
|
infohash: Infohash,
|
||||||
metainfo: Metainfo,
|
metainfo: Metainfo,
|
||||||
size: Bytes,
|
size: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub(crate) struct TorrentSummaryJson {
|
pub struct TorrentSummaryJson {
|
||||||
name: String,
|
pub name: String,
|
||||||
comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
creation_date: Option<u64>,
|
pub creation_date: Option<u64>,
|
||||||
created_by: Option<String>,
|
pub created_by: Option<String>,
|
||||||
source: Option<String>,
|
pub source: Option<String>,
|
||||||
info_hash: String,
|
pub info_hash: String,
|
||||||
torrent_size: u64,
|
pub torrent_size: u64,
|
||||||
content_size: u64,
|
pub content_size: u64,
|
||||||
private: bool,
|
pub private: bool,
|
||||||
tracker: Option<String>,
|
pub tracker: Option<String>,
|
||||||
announce_list: Vec<Vec<String>>,
|
pub announce_list: Vec<Vec<String>>,
|
||||||
update_url: Option<String>,
|
pub update_url: Option<String>,
|
||||||
dht_nodes: Vec<String>,
|
pub dht_nodes: Vec<String>,
|
||||||
piece_size: u64,
|
pub piece_size: u64,
|
||||||
piece_count: usize,
|
pub piece_count: usize,
|
||||||
file_count: usize,
|
pub file_count: usize,
|
||||||
files: Vec<String>,
|
pub files: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TorrentSummary {
|
impl TorrentSummary {
|
||||||
|
@ -37,14 +37,14 @@ impl TorrentSummary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `Info::infohash_lossy` for details on when this function is lossy.
|
/// See `Info::infohash_lossy` for details on when this function is lossy.
|
||||||
pub(crate) fn from_metainfo_lossy(metainfo: Metainfo) -> Result<Self> {
|
pub fn from_metainfo_lossy(metainfo: Metainfo) -> Result<Self> {
|
||||||
let bytes = metainfo.serialize()?;
|
let bytes = metainfo.serialize()?;
|
||||||
let size = Bytes(bytes.len().into_u64());
|
let size = Bytes(bytes.len().into_u64());
|
||||||
let infohash = metainfo.infohash_lossy()?;
|
let infohash = metainfo.infohash_lossy()?;
|
||||||
Ok(Self::new(metainfo, infohash, size))
|
Ok(Self::new(metainfo, infohash, size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_input(input: &Input) -> Result<Self> {
|
pub fn from_input(input: &Input) -> Result<Self> {
|
||||||
let metainfo = Metainfo::from_input(input)?;
|
let metainfo = Metainfo::from_input(input)?;
|
||||||
let infohash = Infohash::from_input(input)?;
|
let infohash = Infohash::from_input(input)?;
|
||||||
let size = Bytes(input.data().len().into_u64());
|
let size = Bytes(input.data().len().into_u64());
|
||||||
|
@ -52,7 +52,7 @@ impl TorrentSummary {
|
||||||
Ok(Self::new(metainfo, infohash, size))
|
Ok(Self::new(metainfo, infohash, size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write(&self, env: &mut Env) -> Result<()> {
|
pub fn write(&self, env: &mut Env) -> Result<()> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
|
|
||||||
if env.out().is_term() {
|
if env.out().is_term() {
|
||||||
|
@ -168,16 +168,16 @@ impl TorrentSummary {
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write_json(&self, env: &mut Env) -> Result<()> {
|
pub fn write_json(&self, env: &mut Env) -> Result<()> {
|
||||||
let data = self.torrent_summary_data();
|
let data = self.torrent_summary_data();
|
||||||
let json = serde_json::to_string(&data).context(error::JsonSerialize)?;
|
let json = serde_json::to_string(&data).context(error::JsonSerialize)?;
|
||||||
outln!(env, "{}", json)?;
|
outln!(env, "{}", json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn torrent_summary_data(&self) -> TorrentSummaryJson {
|
pub fn torrent_summary_data(&self) -> TorrentSummaryJson {
|
||||||
let (file_count, files) = match &self.metainfo.info.mode {
|
let (file_count, files) = match &self.metainfo.info.mode {
|
||||||
Mode::Single { .. } => (1, vec![self.metainfo.info.name.to_owned()]),
|
Mode::Single { .. } => (1, vec![self.metainfo.info.name.clone()]),
|
||||||
Mode::Multiple { files } => (
|
Mode::Multiple { files } => (
|
||||||
files.len(),
|
files.len(),
|
||||||
files
|
files
|
||||||
|
@ -197,7 +197,7 @@ impl TorrentSummary {
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentSummaryJson {
|
TorrentSummaryJson {
|
||||||
name: self.metainfo.info.name.to_owned(),
|
name: self.metainfo.info.name.clone(),
|
||||||
comment: self.metainfo.comment.clone(),
|
comment: self.metainfo.comment.clone(),
|
||||||
creation_date: self.metainfo.creation_date,
|
creation_date: self.metainfo.creation_date,
|
||||||
created_by: self.metainfo.created_by.clone(),
|
created_by: self.metainfo.created_by.clone(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user