Write torrent to stdout if - is passed to --output

type: added
This commit is contained in:
Casey Rodarmor 2020-02-04 21:29:53 -08:00
parent 5a1de1acd2
commit df3326510c
No known key found for this signature in database
GPG Key ID: 556186B153EC6FE0
9 changed files with 102 additions and 27 deletions

View File

@ -17,6 +17,10 @@ impl Capture {
.unwrap() .unwrap()
.to_owned() .to_owned()
} }
pub(crate) fn bytes(&self) -> Vec<u8> {
self.cursor.borrow().get_ref().clone()
}
} }
impl Write for Capture { impl Write for Capture {

View File

@ -49,7 +49,7 @@ pub(crate) use crate::{
bytes::Bytes, env::Env, error::Error, file_info::FileInfo, files::Files, hasher::Hasher, bytes::Bytes, env::Env, error::Error, file_info::FileInfo, files::Files, hasher::Hasher,
info::Info, lint::Lint, linter::Linter, metainfo::Metainfo, mode::Mode, opt::Opt, info::Info, lint::Lint, linter::Linter, metainfo::Metainfo, mode::Mode, opt::Opt,
piece_length_picker::PieceLengthPicker, platform::Platform, style::Style, table::Table, piece_length_picker::PieceLengthPicker, platform::Platform, style::Style, table::Table,
torrent_summary::TorrentSummary, use_color::UseColor, target::Target, torrent_summary::TorrentSummary, use_color::UseColor,
}; };
// test stdlib types // test stdlib types

View File

@ -16,6 +16,7 @@
clippy::result_expect_used, clippy::result_expect_used,
clippy::result_unwrap_used, clippy::result_unwrap_used,
clippy::shadow_reuse, clippy::shadow_reuse,
clippy::too_many_lines,
clippy::unreachable, clippy::unreachable,
clippy::unseparated_literal_suffix, clippy::unseparated_literal_suffix,
clippy::wildcard_enum_match_arm clippy::wildcard_enum_match_arm
@ -76,6 +77,7 @@ mod platform_interface;
mod reckoner; mod reckoner;
mod style; mod style;
mod table; mod table;
mod target;
mod torrent_summary; mod torrent_summary;
mod use_color; mod use_color;

View File

@ -2,19 +2,6 @@ use crate::common::*;
mod torrent; mod torrent;
#[derive(StructOpt)]
pub(crate) enum Subcommand {
Torrent(torrent::Torrent),
}
impl Subcommand {
pub(crate) fn run(self, env: &mut Env, unstable: bool) -> Result<(), Error> {
match self {
Self::Torrent(torrent) => torrent.run(env, unstable),
}
}
}
#[derive(StructOpt)] #[derive(StructOpt)]
#[structopt( #[structopt(
about(consts::ABOUT), about(consts::ABOUT),
@ -51,3 +38,16 @@ impl Opt {
self.subcommand.run(env, self.unstable) self.subcommand.run(env, self.unstable)
} }
} }
#[derive(StructOpt)]
pub(crate) enum Subcommand {
Torrent(torrent::Torrent),
}
impl Subcommand {
pub(crate) fn run(self, env: &mut Env, unstable: bool) -> Result<(), Error> {
match self {
Self::Torrent(torrent) => torrent.run(env, unstable),
}
}
}

View File

@ -47,7 +47,8 @@ Note: Many BitTorrent clients do not implement the behavior described in BEP 12.
name = "INPUT", name = "INPUT",
long = "input", long = "input",
help = "Read torrent contents from `INPUT`.", help = "Read torrent contents from `INPUT`.",
long_help = "Read torrent contents from `INPUT`. If `INPUT` is a file, torrent will be a single-file torrent, otherwise if `INPUT` is a directory, torrent will be a multi-file torrent." long_help = "Read torrent contents from `INPUT`. If `INPUT` is a file, torrent will be a single-file torrent, otherwise if `INPUT` is a directory, torrent will be a multi-file torrent.",
parse(from_os_str)
)] )]
input: PathBuf, input: PathBuf,
#[structopt( #[structopt(
@ -85,9 +86,10 @@ Note: Many BitTorrent clients do not implement the behavior described in BEP 12.
#[structopt( #[structopt(
name = "OUTPUT", name = "OUTPUT",
long = "output", long = "output",
help = "Save `.torrent` file to `OUTPUT`. Defaults to `$INPUT.torrent`." help = "Save `.torrent` file to `OUTPUT`, or `-` for standard output. Defaults to `$INPUT.torrent`.",
parse(from_os_str)
)] )]
output: Option<PathBuf>, output: Option<Target>,
#[structopt( #[structopt(
name = "PIECE-LENGTH", name = "PIECE-LENGTH",
long = "piece-length", long = "piece-length",
@ -175,12 +177,12 @@ impl Create {
let output = self let output = self
.output .output
.as_ref() .as_ref()
.map(|output| env.resolve(&output)) .map(|output| output.resolve(env))
.unwrap_or_else(|| { .unwrap_or_else(|| {
let mut torrent_name = name.to_owned(); let mut torrent_name = name.to_owned();
torrent_name.push_str(".torrent"); torrent_name.push_str(".torrent");
input.parent().unwrap().join(torrent_name) Target::File(input.parent().unwrap().join(torrent_name))
}); });
let private = if self.private { Some(1) } else { None }; let private = if self.private { Some(1) } else { None };
@ -228,12 +230,15 @@ impl Create {
let bytes = metainfo.serialize()?; let bytes = metainfo.serialize()?;
fs::write(&output, &bytes).context(error::Filesystem { path: &output })?; match &output {
Target::File(path) => {
TorrentSummary::from_metainfo(metainfo)?.write(env)?; fs::write(path, &bytes).context(error::Filesystem { path })?;
TorrentSummary::from_metainfo(metainfo)?.write(env)?;
if self.open { if self.open {
Platform::open(&output)?; Platform::open(&path)?;
}
}
Target::Stdio => env.out.write_all(&bytes).context(error::Stdout)?,
} }
Ok(()) Ok(())
@ -890,4 +895,21 @@ Content Size 0 bytes
"; ";
assert_eq!(have, want); assert_eq!(have, want);
} }
#[test]
fn write_to_stdout() {
let mut env = environment(&[
"--input",
"foo",
"--announce",
"http://bar",
"--output",
"-",
]);
fs::write(env.resolve("foo"), "").unwrap();
env.run().unwrap();
let bytes = env.out_bytes();
let value = bencode::Value::decode(&bytes).unwrap();
assert!(matches!(value, bencode::Value::Dict(_)));
}
} }

View File

@ -10,7 +10,8 @@ pub(crate) struct Show {
#[structopt( #[structopt(
name = "TORRENT", name = "TORRENT",
long = "input", long = "input",
help = "Show information about `TORRENT`." help = "Show information about `TORRENT`.",
parse(from_os_str)
)] )]
input: PathBuf, input: PathBuf,
} }

View File

@ -29,7 +29,8 @@ Extract and display values under key paths that match `REGEX`. Subkeys of a benc
long = "input", long = "input",
short = "i", short = "i",
help = "Search `INPUT` for torrents.", help = "Search `INPUT` for torrents.",
long_help = "Search `INPUT` for torrents. May be a directory to search or a single torrent file." long_help = "Search `INPUT` for torrents. May be a directory to search or a single torrent file.",
parse(from_os_str)
)] )]
input: PathBuf, input: PathBuf,
#[structopt( #[structopt(

41
src/target.rs Normal file
View File

@ -0,0 +1,41 @@
use crate::common::*;
#[derive(PartialEq, Debug)]
pub(crate) enum Target {
File(PathBuf),
Stdio,
}
impl Target {
pub(crate) fn resolve(&self, env: &Env) -> Self {
match self {
Self::File(path) => Self::File(env.resolve(path)),
Self::Stdio => Self::Stdio,
}
}
}
impl From<&OsStr> for Target {
fn from(text: &OsStr) -> Self {
if text == OsStr::new("-") {
Self::Stdio
} else {
Self::File(text.into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn file() {
assert_eq!(Target::from(OsStr::new("foo")), Target::File("foo".into()));
}
#[test]
fn stdio() {
assert_eq!(Target::from(OsStr::new("-")), Target::Stdio);
}
}

View File

@ -18,6 +18,10 @@ impl TestEnv {
pub(crate) fn out(&self) -> String { pub(crate) fn out(&self) -> String {
self.out.string() self.out.string()
} }
pub(crate) fn out_bytes(&self) -> Vec<u8> {
self.out.bytes()
}
} }
impl Deref for TestEnv { impl Deref for TestEnv {