Write torrent to stdout if -
is passed to --output
type: added
This commit is contained in:
parent
5a1de1acd2
commit
df3326510c
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
26
src/opt.rs
26
src/opt.rs
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(_)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
41
src/target.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user