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: [`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>_
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# Notes
|
|
@ -53,6 +53,9 @@ pub(crate) use log::trace;
|
|||
// modules
|
||||
pub(crate) use crate::{consts, error, host_port_parse_error};
|
||||
|
||||
// functions
|
||||
pub(crate) use crate::xor_args::xor_args;
|
||||
|
||||
// traits
|
||||
pub(crate) use crate::{
|
||||
input_stream::InputStream, into_u64::IntoU64, into_usize::IntoUsize, invariant::Invariant,
|
||||
|
|
|
@ -100,6 +100,7 @@ mod torrent_summary;
|
|||
mod use_color;
|
||||
mod verifier;
|
||||
mod walker;
|
||||
mod xor_args;
|
||||
|
||||
fn main() {
|
||||
if let Err(code) = Env::main().status() {
|
||||
|
|
|
@ -5,6 +5,16 @@ use create_step::CreateStep;
|
|||
mod create_content;
|
||||
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)]
|
||||
#[structopt(
|
||||
help_message(consts::HELP_MESSAGE),
|
||||
|
@ -115,17 +125,25 @@ Examples:
|
|||
)]
|
||||
include_junk: bool,
|
||||
#[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",
|
||||
short = "i",
|
||||
value_name = "PATH",
|
||||
empty_values(false),
|
||||
empty_values = false,
|
||||
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 \
|
||||
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.",
|
||||
help = INPUT_HELP,
|
||||
)]
|
||||
input: InputTarget,
|
||||
input_flag: Option<InputTarget>,
|
||||
#[structopt(
|
||||
long = "link",
|
||||
help = "Print created torrent `magnet:` URL to standard output"
|
||||
|
@ -144,7 +162,8 @@ Examples:
|
|||
value_name = "TEXT",
|
||||
help = "Set name of torrent to `TEXT`. Defaults to the filename of the argument to `--input`. \
|
||||
Required when `--input -`.",
|
||||
required_if("input", "-")
|
||||
required_if(INPUT_FLAG, "-"),
|
||||
required_if(INPUT_POSITIONAL, "-")
|
||||
)]
|
||||
name: Option<String>,
|
||||
#[structopt(
|
||||
|
@ -192,7 +211,8 @@ Sort in ascending order by size, break ties in descending path order:
|
|||
value_name = "TARGET",
|
||||
empty_values(false),
|
||||
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 `-`. \
|
||||
Defaults to the argument to `--input` with an `.torrent` extension appended. Required \
|
||||
when `--input -`.",
|
||||
|
@ -250,6 +270,13 @@ Sort in ascending order by size, break ties in descending path order:
|
|||
|
||||
impl Create {
|
||||
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();
|
||||
linter.allow(self.allowed_lints.iter().cloned());
|
||||
|
||||
|
@ -281,10 +308,10 @@ impl Create {
|
|||
};
|
||||
|
||||
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)?;
|
||||
|
||||
|
@ -398,7 +425,7 @@ impl Create {
|
|||
|
||||
#[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();
|
||||
|
||||
assert_eq!(deserialized, metainfo);
|
||||
|
@ -451,6 +478,79 @@ mod tests {
|
|||
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]
|
||||
fn require_input_present() {
|
||||
let mut env = test_env! {
|
||||
|
@ -507,6 +607,30 @@ mod tests {
|
|||
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]
|
||||
fn input_dot() {
|
||||
let mut env = test_env! {
|
||||
|
@ -526,10 +650,12 @@ mod tests {
|
|||
}
|
||||
};
|
||||
env.assert_ok();
|
||||
// let metainfo = env.load_metainfo("../dir.torrent");
|
||||
// assert_eq!(metainfo.info.name, "dir");
|
||||
// assert_matches!(metainfo.info.mode, Mode::Multiple{files} if files.len()
|
||||
// == 1);
|
||||
let metainfo = env.load_metainfo("../dir.torrent");
|
||||
assert_eq!(metainfo.info.name, "dir");
|
||||
assert_matches!(
|
||||
metainfo.info.mode,
|
||||
Mode::Multiple{files} if files.len() == 1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2700,7 +2826,7 @@ Content Size 9 bytes
|
|||
],
|
||||
tree: {},
|
||||
};
|
||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
||||
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2718,7 +2844,7 @@ Content Size 9 bytes
|
|||
],
|
||||
tree: {},
|
||||
};
|
||||
assert!(matches!(env.run(), Err(Error::Clap { .. })));
|
||||
assert_matches!(env.run(), Err(Error::Clap { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -11,8 +11,8 @@ pub(crate) struct CreateContent {
|
|||
}
|
||||
|
||||
impl CreateContent {
|
||||
pub(crate) fn from_create(create: &Create, env: &mut Env) -> Result<Self> {
|
||||
match &create.input {
|
||||
pub(crate) fn from_create(create: &Create, input: &InputTarget, env: &mut Env) -> Result<Self> {
|
||||
match input {
|
||||
InputTarget::Path(path) => {
|
||||
let spinner = if env.err().is_styled_term() {
|
||||
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