Compare commits

..

No commits in common. "ea3b22fcccc7aecaf28f8005478a1ad957332a30" and "2346c30fec91633444accb83837a132379e0ea00" have entirely different histories.

29 changed files with 473 additions and 655 deletions

View File

@ -39,7 +39,7 @@ jobs:
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
env: env:
RUSTFLAGS: --deny warnings RUSTFLAGS: "-D warnings"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -63,9 +63,14 @@ 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 Rust Toolchain Components - name: Install Stable
run: | uses: actions-rs/toolchain@v1
rustup component add clippy rustfmt with:
toolchain: stable
target: ${{matrix.target}}
profile: minimal
components: clippy, rustfmt
override: true
- name: Info - name: Info
run: | run: |

649
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -26,13 +26,16 @@ case $os in
;; ;;
esac esac
RUSTFLAGS='--deny warnings --codegen target-feature=+crt-static' \ case $os in
cargo build --bin $bin --target $target --release ubuntu-latest | macos-latest)
executable=target/$target/release/$bin cargo rustc --bin $bin --target $target --release
executable=target/$target/release/$bin
if [[ $os == windows-2016 ]]; then ;;
executable=$executable.exe windows-latest)
fi cargo rustc --bin $bin --target $target --release -- -C target-feature="+crt-static"
executable=target/$target/release/$bin.exe
;;
esac
echo "Building completions..." echo "Building completions..."
cargo run --package gen -- --bin $executable completion-scripts cargo run --package gen -- --bin $executable completion-scripts

View File

@ -1 +0,0 @@
1.51.0

View File

@ -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;
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)]
#[serde(transparent)] #[serde(transparent)]
pub struct Bytes(pub(crate) u64); #[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)]
pub(crate) 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.clone(), suffix: suffix.to_owned(),
}) })
} }
}; };

View File

@ -1,6 +1,6 @@
use crate::common::*; use crate::common::*;
pub struct Env { pub(crate) 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

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(Debug, Snafu)] #[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))] #[snafu(visibility(pub(crate)))]
pub enum Error { pub(crate) 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))]

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)]
#[serde(transparent)] #[serde(transparent)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)]
pub(crate) struct FilePath { pub(crate) struct FilePath {
components: Vec<String>, components: Vec<String>,
} }

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct HostPort { pub(crate) struct HostPort {
host: Host, host: Host,
port: u16, port: u16,
} }

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(Debug, Snafu)] #[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))] #[snafu(visibility(pub(crate)))]
pub enum HostPortParseError { pub(crate) enum HostPortParseError {
#[snafu(display("Failed to parse host `{}`: {}", text, source))] #[snafu(display("Failed to parse host `{}`: {}", text, source))]
Host { Host {
text: String, text: String,

View File

@ -1,22 +1,22 @@
use crate::common::*; use crate::common::*;
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub struct Info { pub(crate) 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 private: Option<bool>, pub(crate) private: Option<bool>,
#[serde(rename = "piece length")] #[serde(rename = "piece length")]
pub piece_length: Bytes, pub(crate) piece_length: Bytes,
pub name: String, pub(crate) 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 source: Option<String>, pub(crate) 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 struct Info {
with = "unwrap_or_skip", with = "unwrap_or_skip",
rename = "update-url" rename = "update-url"
)] )]
pub update_url: Option<Url>, pub(crate) update_url: Option<Url>,
} }
impl Info { impl Info {

View File

@ -1,12 +1,12 @@
use crate::common::*; use crate::common::*;
#[derive(Debug, Eq, PartialEq, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Infohash { pub(crate) struct Infohash {
pub inner: Sha1Digest, inner: Sha1Digest,
} }
impl Infohash { impl Infohash {
pub fn from_input(input: &Input) -> Result<Infohash, Error> { pub(crate) 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 fn from_bencoded_info_dict(info: &[u8]) -> Infohash { pub(crate) fn from_bencoded_info_dict(info: &[u8]) -> Infohash {
Infohash { Infohash {
inner: Sha1Digest::from_data(info), inner: Sha1Digest::from_data(info),
} }

View File

@ -1,20 +1,20 @@
use crate::common::*; use crate::common::*;
pub struct Input { pub(crate) struct Input {
source: InputTarget, source: InputTarget,
data: Vec<u8>, data: Vec<u8>,
} }
impl Input { impl Input {
pub fn new(source: InputTarget, data: Vec<u8>) -> Input { pub(crate) fn new(source: InputTarget, data: Vec<u8>) -> Input {
Self { source, data } Self { source, data }
} }
pub fn data(&self) -> &[u8] { pub(crate) fn data(&self) -> &[u8] {
&self.data &self.data
} }
pub fn source(&self) -> &InputTarget { pub(crate) fn source(&self) -> &InputTarget {
&self.source &self.source
} }

View File

@ -1,13 +1,13 @@
use crate::common::*; use crate::common::*;
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub enum InputTarget { pub(crate) enum InputTarget {
Path(PathBuf), Path(PathBuf),
Stdin, Stdin,
} }
impl InputTarget { impl InputTarget {
pub fn try_from_os_str(text: &OsStr) -> Result<Self, OsString> { pub(crate) 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()))

View File

@ -41,85 +41,85 @@
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
pub mod assert_matches; mod assert_matches;
#[macro_use] #[macro_use]
pub mod errln; mod errln;
#[macro_use] #[macro_use]
pub mod err; mod err;
#[macro_use] #[macro_use]
pub mod out; mod out;
#[macro_use] #[macro_use]
pub mod outln; mod outln;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
pub mod test_env; mod test_env;
#[cfg(test)] #[cfg(test)]
pub mod test_env_builder; mod test_env_builder;
#[cfg(test)] #[cfg(test)]
pub mod capture; mod capture;
pub mod arguments; mod arguments;
pub mod bytes; mod bytes;
pub mod common; mod common;
pub mod consts; mod consts;
pub mod env; mod env;
pub mod error; mod error;
pub mod file_error; mod file_error;
pub mod file_info; mod file_info;
pub mod file_path; mod file_path;
pub mod file_status; mod file_status;
pub mod files; mod files;
pub mod hasher; mod hasher;
pub mod host_port; mod host_port;
pub mod host_port_parse_error; mod host_port_parse_error;
pub mod info; mod info;
pub mod infohash; mod infohash;
pub mod input; mod input;
pub mod input_stream; mod input_stream;
pub mod input_target; mod input_target;
pub mod into_u64; mod into_u64;
pub mod into_usize; mod into_usize;
pub mod invariant; mod invariant;
pub mod lint; mod lint;
pub mod linter; mod linter;
pub mod magnet_link; mod magnet_link;
pub mod magnet_link_parse_error; mod magnet_link_parse_error;
pub mod md5_digest; mod md5_digest;
pub mod metainfo; mod metainfo;
pub mod metainfo_error; mod metainfo_error;
pub mod mode; mod mode;
pub mod options; mod options;
pub mod output_stream; mod output_stream;
pub mod output_target; mod output_target;
pub mod piece_length_picker; mod piece_length_picker;
pub mod piece_list; mod piece_list;
pub mod platform; mod platform;
pub mod platform_interface; mod platform_interface;
pub mod print; mod print;
pub mod reckoner; mod reckoner;
pub mod run; mod run;
pub mod sha1_digest; mod sha1_digest;
pub mod shell; mod shell;
pub mod sort_key; mod sort_key;
pub mod sort_order; mod sort_order;
pub mod sort_spec; mod sort_spec;
pub mod status; mod status;
pub mod step; mod step;
pub mod style; mod style;
pub mod subcommand; mod subcommand;
pub mod table; mod table;
pub mod torrent_summary; mod torrent_summary;
pub mod use_color; mod use_color;
pub mod verifier; mod verifier;
pub mod walker; mod walker;
pub mod xor_args; mod xor_args;
#[cfg(feature = "bench")] #[cfg(feature = "bench")]
pub mod bench; pub mod bench;

View File

@ -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 {

View File

@ -1,17 +1,17 @@
use crate::common::*; use crate::common::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct MagnetLink { pub(crate) struct MagnetLink {
pub infohash: Infohash, infohash: Infohash,
pub name: Option<String>, name: Option<String>,
pub peers: Vec<HostPort>, peers: Vec<HostPort>,
pub trackers: Vec<Url>, trackers: Vec<Url>,
pub indices: BTreeSet<u64>, 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 fn from_metainfo_lossy(metainfo: &Metainfo) -> Result<MagnetLink> { pub(crate) 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 fn with_infohash(infohash: Infohash) -> Self { pub(crate) 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 fn set_name(&mut self, name: impl Into<String>) { pub(crate) fn set_name(&mut self, name: impl Into<String>) {
self.name = Some(name.into()); self.name = Some(name.into());
} }
pub fn add_peer(&mut self, peer: HostPort) { pub(crate) fn add_peer(&mut self, peer: HostPort) {
self.peers.push(peer); self.peers.push(peer);
} }
pub fn add_tracker(&mut self, tracker: Url) { pub(crate) fn add_tracker(&mut self, tracker: Url) {
self.trackers.push(tracker); self.trackers.push(tracker);
} }
pub fn add_index(&mut self, index: u64) { pub(crate) fn add_index(&mut self, index: u64) {
self.indices.insert(index); self.indices.insert(index);
} }
pub fn to_url(&self) -> Url { pub(crate) 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
} }
pub fn parse(text: &str) -> Result<Self, MagnetLinkParseError> { 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" {

View File

@ -2,7 +2,7 @@ use crate::common::*;
#[derive(Debug, Snafu)] #[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))] #[snafu(visibility(pub(crate)))]
pub enum MagnetLinkParseError { pub(crate) 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,

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)]
#[serde(transparent)] #[serde(transparent)]
#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)]
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],

View File

@ -1,68 +1,68 @@
use crate::common::*; use crate::common::*;
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub struct Metainfo { pub(crate) 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 announce: Option<String>, pub(crate) 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 announce_list: Option<Vec<Vec<String>>>, pub(crate) 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 comment: Option<String>, pub(crate) 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 created_by: Option<String>, pub(crate) 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 creation_date: Option<u64>, pub(crate) 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 encoding: Option<String>, pub(crate) encoding: Option<String>,
pub info: Info, pub(crate) 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 nodes: Option<Vec<HostPort>>, pub(crate) nodes: Option<Vec<HostPort>>,
} }
impl Metainfo { impl Metainfo {
pub fn from_input(input: &Input) -> Result<Metainfo> { pub(crate) fn from_input(input: &Input) -> Result<Metainfo> {
Self::deserialize(input.source(), input.data()) Self::deserialize(input.source(), input.data())
} }
pub fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> { pub(crate) 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 fn serialize(&self) -> Result<Vec<u8>, Error> { pub(crate) 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 fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a { pub(crate) 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()

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum MetainfoError { pub(crate) enum MetainfoError {
Type, Type,
InfoMissing, InfoMissing,
InfoType, InfoType,

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
#[serde(untagged)] #[serde(untagged)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub(crate) enum Mode { pub(crate) enum Mode {
Single { Single {
length: Bytes, length: Bytes,

View File

@ -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 {

View File

@ -1,22 +1,22 @@
use crate::common::*; use crate::common::*;
#[derive(Debug, Eq, PartialEq, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct Sha1Digest { pub(crate) struct Sha1Digest {
pub bytes: [u8; Self::LENGTH], bytes: [u8; Self::LENGTH],
} }
impl Sha1Digest { impl Sha1Digest {
pub const LENGTH: usize = 20; pub(crate) const LENGTH: usize = 20;
pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self { pub(crate) fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self {
Sha1Digest { bytes } Sha1Digest { bytes }
} }
pub fn bytes(self) -> [u8; Self::LENGTH] { pub(crate) fn bytes(self) -> [u8; Self::LENGTH] {
self.bytes self.bytes
} }
pub fn from_data(data: impl AsRef<[u8]>) -> Self { pub(crate) fn from_data(data: impl AsRef<[u8]>) -> Self {
Sha1::from(data).digest().into() Sha1::from(data).digest().into()
} }
} }

View File

@ -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 enum Shell { pub(crate) 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())
} }
} }

View File

@ -338,7 +338,9 @@ 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 { path: path.clone() }); return Err(Error::OutputExists {
path: path.to_owned(),
});
} }
} }
@ -1149,7 +1151,7 @@ mod tests {
length: Bytes(3), length: Bytes(3),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1180,7 +1182,7 @@ mod tests {
length: Bytes(4), length: Bytes(4),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1211,7 +1213,7 @@ mod tests {
length: Bytes(4), length: Bytes(4),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1256,7 +1258,7 @@ mod tests {
}, },
], ],
} }
); )
} }
#[test] #[test]
@ -1283,7 +1285,7 @@ mod tests {
length: Bytes(3), length: Bytes(3),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1317,7 +1319,7 @@ mod tests {
length: Bytes(3), length: Bytes(3),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1344,7 +1346,7 @@ mod tests {
length: Bytes(0), length: Bytes(0),
md5sum: None, md5sum: None,
} }
); )
} }
#[test] #[test]
@ -1365,7 +1367,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]

View File

@ -16,19 +16,7 @@ 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",
@ -36,7 +24,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 the torrent info dictionary." of torrent info dictionary."
)] )]
content: Option<PathBuf>, content: Option<PathBuf>,
#[structopt( #[structopt(
@ -76,19 +64,13 @@ impl Verify {
let metainfo = Metainfo::from_input(&input)?; let metainfo = Metainfo::from_input(&input)?;
let content = self let content = self.content.as_ref().map_or_else(
.content || match target {
.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()
@ -155,20 +137,6 @@ 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! {
@ -355,62 +323,6 @@ 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! {

View File

@ -37,19 +37,19 @@ macro_rules! test_env {
pub(crate) struct TestEnv { pub(crate) struct TestEnv {
env: Env, env: Env,
err: Capture,
out: Capture,
#[allow(unused)] #[allow(unused)]
tempdir: TempDir, tempdir: TempDir,
err: Capture,
out: Capture,
} }
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 {
env,
err,
out,
tempdir, tempdir,
err,
env,
out,
} }
} }
@ -77,14 +77,6 @@ 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()
} }

View File

@ -1,30 +1,30 @@
use crate::common::*; use crate::common::*;
pub struct TorrentSummary { pub(crate) struct TorrentSummary {
infohash: Infohash, infohash: Infohash,
metainfo: Metainfo, metainfo: Metainfo,
size: Bytes, size: Bytes,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct TorrentSummaryJson { pub(crate) struct TorrentSummaryJson {
pub name: String, name: String,
pub comment: Option<String>, comment: Option<String>,
pub creation_date: Option<u64>, creation_date: Option<u64>,
pub created_by: Option<String>, created_by: Option<String>,
pub source: Option<String>, source: Option<String>,
pub info_hash: String, info_hash: String,
pub torrent_size: u64, torrent_size: u64,
pub content_size: u64, content_size: u64,
pub private: bool, private: bool,
pub tracker: Option<String>, tracker: Option<String>,
pub announce_list: Vec<Vec<String>>, announce_list: Vec<Vec<String>>,
pub update_url: Option<String>, update_url: Option<String>,
pub dht_nodes: Vec<String>, dht_nodes: Vec<String>,
pub piece_size: u64, piece_size: u64,
pub piece_count: usize, piece_count: usize,
pub file_count: usize, file_count: usize,
pub files: Vec<String>, 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 fn from_metainfo_lossy(metainfo: Metainfo) -> Result<Self> { pub(crate) 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 fn from_input(input: &Input) -> Result<Self> { pub(crate) 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 fn write(&self, env: &mut Env) -> Result<()> { pub(crate) 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 fn write_json(&self, env: &mut Env) -> Result<()> { pub(crate) 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(())
} }
pub fn torrent_summary_data(&self) -> TorrentSummaryJson { 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.clone()]), Mode::Single { .. } => (1, vec![self.metainfo.info.name.to_owned()]),
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.clone(), name: self.metainfo.info.name.to_owned(),
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(),