Take input to imdl torrent create
as positional
Allow taking the `--input` argument to `imdl torrent create` as a positional argument, so the following now works: imdl torrent create foo Taking input by flag `--input` still works. type: changed fixes: - https://github.com/casey/intermodal/issues/375
This commit is contained in:
parent
c22df5a083
commit
5ba885dbc4
|
@ -2,9 +2,10 @@ Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
|
||||||
UNRELEASED - 2020-04-21
|
UNRELEASED - 2020-04-22
|
||||||
-----------------------
|
-----------------------
|
||||||
- :wrench: [`xxxxxxxxxxxx`](https://github.com/casey/intermodal/commits/master) Don't commit man pages - _Casey Rodarmor <casey@rodarmor.com>_
|
- :zap: [`xxxxxxxxxxxx`](https://github.com/casey/intermodal/commits/master) Take input to `imdl torrent create` as positional - Fixes [#375](https://github.com/casey/intermodal/issues/375) - _Casey Rodarmor <casey@rodarmor.com>_
|
||||||
|
- :wrench: [`c22df5a08326`](https://github.com/casey/intermodal/commit/c22df5a083265b03abd5531b1f5b2aad60aa68cd) Don't commit man pages - _Casey Rodarmor <casey@rodarmor.com>_
|
||||||
- :wrench: [`4d67d3a10d17`](https://github.com/casey/intermodal/commit/4d67d3a10d17db3c63af092a936eb5994ee107b1) Don't commit the book - _Casey Rodarmor <casey@rodarmor.com>_
|
- :wrench: [`4d67d3a10d17`](https://github.com/casey/intermodal/commit/4d67d3a10d17db3c63af092a936eb5994ee107b1) Don't commit the book - _Casey Rodarmor <casey@rodarmor.com>_
|
||||||
- :wrench: [`28114c3d64dd`](https://github.com/casey/intermodal/commit/28114c3d64dde5e0275c936b0019eaf4760ba559) Don't commit shell completion scripts - _Casey Rodarmor <casey@rodarmor.com>_
|
- :wrench: [`28114c3d64dd`](https://github.com/casey/intermodal/commit/28114c3d64dde5e0275c936b0019eaf4760ba559) Don't commit shell completion scripts - _Casey Rodarmor <casey@rodarmor.com>_
|
||||||
- :art: [`4f4464e3a2a7`](https://github.com/casey/intermodal/commit/4f4464e3a2a7f4aaffea8dbe38dd110ad9be4393) Get `st_flags` from `MetadataExt` on MacOS - _Casey Rodarmor <casey@rodarmor.com>_
|
- :art: [`4f4464e3a2a7`](https://github.com/casey/intermodal/commit/4f4464e3a2a7f4aaffea8dbe38dd110ad9be4393) Get `st_flags` from `MetadataExt` on MacOS - _Casey Rodarmor <casey@rodarmor.com>_
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
# Notes
|
|
|
@ -53,6 +53,9 @@ pub(crate) use log::trace;
|
||||||
// modules
|
// modules
|
||||||
pub(crate) use crate::{consts, error, host_port_parse_error};
|
pub(crate) use crate::{consts, error, host_port_parse_error};
|
||||||
|
|
||||||
|
// functions
|
||||||
|
pub(crate) use crate::xor_args::xor_args;
|
||||||
|
|
||||||
// traits
|
// traits
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
input_stream::InputStream, into_u64::IntoU64, into_usize::IntoUsize, invariant::Invariant,
|
input_stream::InputStream, into_u64::IntoU64, into_usize::IntoUsize, invariant::Invariant,
|
||||||
|
|
|
@ -100,6 +100,7 @@ mod torrent_summary;
|
||||||
mod use_color;
|
mod use_color;
|
||||||
mod verifier;
|
mod verifier;
|
||||||
mod walker;
|
mod walker;
|
||||||
|
mod xor_args;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(code) = Env::main().status() {
|
if let Err(code) = Env::main().status() {
|
||||||
|
|
|
@ -5,6 +5,16 @@ use create_step::CreateStep;
|
||||||
mod create_content;
|
mod create_content;
|
||||||
mod create_step;
|
mod create_step;
|
||||||
|
|
||||||
|
const INPUT_HELP: &str = "Read torrent contents from `PATH`. If `PATH` is a file, torrent will be \
|
||||||
|
a single-file torrent. If `PATH` is a directory, torrent will be a \
|
||||||
|
multi-file torrent. If `PATH` is `-`, read from standard input. Piece \
|
||||||
|
length defaults to 256KiB when reading from standard input if \
|
||||||
|
`--piece-length` is not given.";
|
||||||
|
|
||||||
|
const INPUT_FLAG: &str = "input-flag";
|
||||||
|
|
||||||
|
const INPUT_POSITIONAL: &str = "<INPUT>";
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(
|
#[structopt(
|
||||||
help_message(consts::HELP_MESSAGE),
|
help_message(consts::HELP_MESSAGE),
|
||||||
|
@ -115,17 +125,25 @@ Examples:
|
||||||
)]
|
)]
|
||||||
include_junk: bool,
|
include_junk: bool,
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
name = INPUT_POSITIONAL,
|
||||||
|
value_name = "INPUT",
|
||||||
|
empty_values = false,
|
||||||
|
required_unless = INPUT_FLAG,
|
||||||
|
conflicts_with = INPUT_FLAG,
|
||||||
|
parse(try_from_os_str = InputTarget::try_from_os_str),
|
||||||
|
help = INPUT_HELP,
|
||||||
|
)]
|
||||||
|
input_positional: Option<InputTarget>,
|
||||||
|
#[structopt(
|
||||||
|
name = INPUT_FLAG,
|
||||||
long = "input",
|
long = "input",
|
||||||
short = "i",
|
short = "i",
|
||||||
value_name = "PATH",
|
value_name = "PATH",
|
||||||
empty_values(false),
|
empty_values = false,
|
||||||
parse(try_from_os_str = InputTarget::try_from_os_str),
|
parse(try_from_os_str = InputTarget::try_from_os_str),
|
||||||
help = "Read torrent contents from `PATH`. If `PATH` is a file, torrent will be a single-file \
|
help = INPUT_HELP,
|
||||||
torrent. If `PATH` is a directory, torrent will be a multi-file torrent. If `PATH` \
|
|
||||||
is `-`, read from standard input. Piece length defaults to 256KiB when reading from \
|
|
||||||
standard input if `--piece-length` is not given.",
|
|
||||||
)]
|
)]
|
||||||
input: InputTarget,
|
input_flag: Option<InputTarget>,
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long = "link",
|
long = "link",
|
||||||
help = "Print created torrent `magnet:` URL to standard output"
|
help = "Print created torrent `magnet:` URL to standard output"
|
||||||
|
@ -144,7 +162,8 @@ Examples:
|
||||||
value_name = "TEXT",
|
value_name = "TEXT",
|
||||||
help = "Set name of torrent to `TEXT`. Defaults to the filename of the argument to `--input`. \
|
help = "Set name of torrent to `TEXT`. Defaults to the filename of the argument to `--input`. \
|
||||||
Required when `--input -`.",
|
Required when `--input -`.",
|
||||||
required_if("input", "-")
|
required_if(INPUT_FLAG, "-"),
|
||||||
|
required_if(INPUT_POSITIONAL, "-")
|
||||||
)]
|
)]
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
#[structopt(
|
#[structopt(
|
||||||
|
@ -192,7 +211,8 @@ Sort in ascending order by size, break ties in descending path order:
|
||||||
value_name = "TARGET",
|
value_name = "TARGET",
|
||||||
empty_values(false),
|
empty_values(false),
|
||||||
parse(try_from_os_str = OutputTarget::try_from_os_str),
|
parse(try_from_os_str = OutputTarget::try_from_os_str),
|
||||||
required_if("input", "-"),
|
required_if(INPUT_FLAG, "-"),
|
||||||
|
required_if(INPUT_POSITIONAL, "-"),
|
||||||
help = "Save `.torrent` file to `TARGET`, or print to standard output if `TARGET` is `-`. \
|
help = "Save `.torrent` file to `TARGET`, or print to standard output if `TARGET` is `-`. \
|
||||||
Defaults to the argument to `--input` with an `.torrent` extension appended. Required \
|
Defaults to the argument to `--input` with an `.torrent` extension appended. Required \
|
||||||
when `--input -`.",
|
when `--input -`.",
|
||||||
|
@ -250,6 +270,13 @@ Sort in ascending order by size, break ties in descending path order:
|
||||||
|
|
||||||
impl Create {
|
impl Create {
|
||||||
pub(crate) fn run(self, env: &mut Env, options: &Options) -> Result<(), Error> {
|
pub(crate) fn run(self, env: &mut Env, options: &Options) -> Result<(), Error> {
|
||||||
|
let input = xor_args(
|
||||||
|
"input_positional",
|
||||||
|
&self.input_positional,
|
||||||
|
"input_flag",
|
||||||
|
&self.input_flag,
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut linter = Linter::new();
|
let mut linter = Linter::new();
|
||||||
linter.allow(self.allowed_lints.iter().cloned());
|
linter.allow(self.allowed_lints.iter().cloned());
|
||||||
|
|
||||||
|
@ -281,10 +308,10 @@ impl Create {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !options.quiet {
|
if !options.quiet {
|
||||||
CreateStep::Searching { input: &self.input }.print(env)?;
|
CreateStep::Searching { input: &input }.print(env)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = CreateContent::from_create(&self, env)?;
|
let content = CreateContent::from_create(&self, &input, env)?;
|
||||||
|
|
||||||
let output = content.output.resolve(env)?;
|
let output = content.output.resolve(env)?;
|
||||||
|
|
||||||
|
@ -398,7 +425,7 @@ impl Create {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
{
|
||||||
if let InputTarget::Path(path) = &self.input {
|
if let InputTarget::Path(path) = &input {
|
||||||
let deserialized = bendy::serde::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
let deserialized = bendy::serde::de::from_bytes::<Metainfo>(&bytes).unwrap();
|
||||||
|
|
||||||
assert_eq!(deserialized, metainfo);
|
assert_eq!(deserialized, metainfo);
|
||||||
|
@ -451,6 +478,79 @@ mod tests {
|
||||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_arguments_conflict() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn require_name_if_input_flag_stdin() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"-",
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn require_name_if_input_positional_stdin() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"-",
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn require_output_if_input_flag_stdin() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"--input",
|
||||||
|
"-",
|
||||||
|
"--name",
|
||||||
|
"foo",
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn require_output_if_input_positional_stdin() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"-",
|
||||||
|
"--name",
|
||||||
|
"foo",
|
||||||
|
],
|
||||||
|
tree: {},
|
||||||
|
};
|
||||||
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn require_input_present() {
|
fn require_input_present() {
|
||||||
let mut env = test_env! {
|
let mut env = test_env! {
|
||||||
|
@ -507,6 +607,30 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_positional() {
|
||||||
|
let mut env = test_env! {
|
||||||
|
args: [
|
||||||
|
"torrent",
|
||||||
|
"create",
|
||||||
|
"foo",
|
||||||
|
],
|
||||||
|
tree: {
|
||||||
|
foo: {
|
||||||
|
bar: "",
|
||||||
|
baz: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
env.assert_ok();
|
||||||
|
let metainfo = env.load_metainfo("foo.torrent");
|
||||||
|
assert_eq!(metainfo.info.name, "foo");
|
||||||
|
assert_matches!(
|
||||||
|
metainfo.info.mode,
|
||||||
|
Mode::Multiple{files} if files.len() == 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn input_dot() {
|
fn input_dot() {
|
||||||
let mut env = test_env! {
|
let mut env = test_env! {
|
||||||
|
@ -526,10 +650,12 @@ mod tests {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
env.assert_ok();
|
env.assert_ok();
|
||||||
// let metainfo = env.load_metainfo("../dir.torrent");
|
let metainfo = env.load_metainfo("../dir.torrent");
|
||||||
// assert_eq!(metainfo.info.name, "dir");
|
assert_eq!(metainfo.info.name, "dir");
|
||||||
// assert_matches!(metainfo.info.mode, Mode::Multiple{files} if files.len()
|
assert_matches!(
|
||||||
// == 1);
|
metainfo.info.mode,
|
||||||
|
Mode::Multiple{files} if files.len() == 1
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2700,7 +2826,7 @@ Content Size 9 bytes
|
||||||
],
|
],
|
||||||
tree: {},
|
tree: {},
|
||||||
};
|
};
|
||||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2718,7 +2844,7 @@ Content Size 9 bytes
|
||||||
],
|
],
|
||||||
tree: {},
|
tree: {},
|
||||||
};
|
};
|
||||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -11,8 +11,8 @@ pub(crate) struct CreateContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateContent {
|
impl CreateContent {
|
||||||
pub(crate) fn from_create(create: &Create, env: &mut Env) -> Result<Self> {
|
pub(crate) fn from_create(create: &Create, input: &InputTarget, env: &mut Env) -> Result<Self> {
|
||||||
match &create.input {
|
match input {
|
||||||
InputTarget::Path(path) => {
|
InputTarget::Path(path) => {
|
||||||
let spinner = if env.err().is_styled_term() {
|
let spinner = if env.err().is_styled_term() {
|
||||||
let style = ProgressStyle::default_spinner()
|
let style = ProgressStyle::default_spinner()
|
||||||
|
|
17
src/xor_args.rs
Normal file
17
src/xor_args.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) fn xor_args<T: Clone>(
|
||||||
|
a_name: &str,
|
||||||
|
a: &Option<T>,
|
||||||
|
b_name: &str,
|
||||||
|
b: &Option<T>,
|
||||||
|
) -> Result<T> {
|
||||||
|
let target = a.as_ref().xor(b.as_ref()).ok_or_else(|| {
|
||||||
|
Error::internal(format!(
|
||||||
|
"Expected exactly one of the arguments `{}` or `{}` to be set",
|
||||||
|
a_name, b_name
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(target.clone())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user