diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa0f69702..2c5276607 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,13 +104,15 @@ jobs: - name: Install dependencies for musl libc run: | sudo apt-get update - sudo apt-get install musl-tools + sudo apt-get install help2man musl-tools - name: Run cargo build uses: actions-rs/cargo@v1 with: command: build args: --release --target x86_64-unknown-linux-musl + env: + GEN_COMPLETIONS: 1 - name: Run cargo test uses: actions-rs/cargo@v1 @@ -118,6 +120,11 @@ jobs: command: test args: --target x86_64-unknown-linux-musl + - name: Build man page and find completions + run: | + help2man target/x86_64-unknown-linux-musl/release/ouch > ouch.1 + cp -r target/x86_64-unknown-linux-musl/release/build/ouch-*/out/completions . + - name: Strip binary run: strip target/x86_64-unknown-linux-musl/release/ouch @@ -127,6 +134,17 @@ jobs: name: 'ouch-x86_64-linux-musl' path: target/x86_64-unknown-linux-musl/release/ouch + - name: Upload completions + uses: actions/upload-artifact@v2 + with: + name: completions + path: completions + + - name: Upload man page + uses: actions/upload-artifact@v2 + with: + name: ouch.1 + path: ouch.1 x86_64_glibc: name: Ubuntu 20.04 (glibc) diff --git a/Cargo.lock b/Cargo.lock index c098ebdb9..75733f18e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_generate" +version = "3.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "097ab5db1c3417442270cd57c8dd39f6c3114d3ce09d595f9efddbb1fcfaa799" +dependencies = [ + "clap", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -289,6 +298,7 @@ dependencies = [ "atty", "bzip2", "clap", + "clap_generate", "flate2", "fs-err", "infer", diff --git a/Cargo.toml b/Cargo.toml index 248f33a4a..f290ac055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,10 @@ zip = { version = "0.5.13", default-features = false, features = ["defl flate2 = { version = "1.0.22", default-features = false, features = ["zlib"] } zstd = { version = "0.9.0", default-features = false, features = ["thin"] } +[build-dependencies] +clap = "=3.0.0-beta.5" +clap_generate = "=3.0.0-beta.5" + [dev-dependencies] tempfile = "3.2.0" infer = "0.5.0" diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..53721547e --- /dev/null +++ b/build.rs @@ -0,0 +1,22 @@ +use clap::{ArgEnum, IntoApp}; +use clap_generate::{generate_to, Shell}; + +use std::{env, fs::create_dir_all, path::Path}; + +include!("src/opts.rs"); + +fn main() { + println!("cargo:rerun-if-env-changed=GEN_COMPLETIONS"); + + if env::var_os("GEN_COMPLETIONS") != Some("1".into()) { + return; + } + + let out = &Path::new(&env::var_os("OUT_DIR").unwrap()).join("completions"); + create_dir_all(out).unwrap(); + let app = &mut Opts::into_app(); + + for shell in Shell::value_variants() { + generate_to(*shell, app, "ouch", out).unwrap(); + } +} diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 6b2327822..4c0d96ec0 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -13,7 +13,8 @@ use walkdir::WalkDir; use crate::{ error::FinalError, info, - utils::{self, Bytes, QuestionPolicy}, + utils::{self, Bytes}, + QuestionPolicy, }; pub fn unpack_archive( diff --git a/src/archive/zip.rs b/src/archive/zip.rs index 4ef9a8500..a43af539d 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -13,7 +13,8 @@ use zip::{self, read::ZipFile, ZipArchive}; use crate::{ info, - utils::{self, dir_is_empty, strip_cur_dir, Bytes, QuestionPolicy}, + utils::{self, dir_is_empty, strip_cur_dir, Bytes}, + QuestionPolicy, }; use self::utf8::get_invalid_utf8_paths; diff --git a/src/cli.rs b/src/cli.rs index e0b8441ca..4ef39ecf5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,52 +5,10 @@ use std::{ vec::Vec, }; -use clap::{Parser, ValueHint}; +use clap::Parser; use fs_err as fs; -pub use crate::utils::QuestionPolicy; -use crate::Error; - -#[derive(Parser, Debug)] -#[clap(version, about)] -pub struct Opts { - /// Skip overwrite questions positively. - #[clap(short, long, conflicts_with = "no")] - pub yes: bool, - - /// Skip overwrite questions negatively. - #[clap(short, long)] - pub no: bool, - - #[clap(subcommand)] - pub cmd: Subcommand, -} - -#[derive(Parser, PartialEq, Eq, Debug)] -pub enum Subcommand { - /// Compress files. Alias: c - #[clap(alias = "c")] - Compress { - /// Files to be compressed - #[clap(required = true, min_values = 1)] - files: Vec, - - /// The resulting file. Its extensions specify how the files will be compressed and they need to be supported - #[clap(required = true, value_hint = ValueHint::FilePath)] - output: PathBuf, - }, - /// Compress files. Alias: d - #[clap(alias = "d")] - Decompress { - /// Files to be decompressed - #[clap(required = true, min_values = 1)] - files: Vec, - - /// Decompress files in a directory other than the current - #[clap(short, long, value_hint = ValueHint::DirPath)] - output: Option, - }, -} +use crate::{Error, Opts, QuestionPolicy, Subcommand}; impl Opts { /// A helper method that calls `clap::Parser::parse` and then translates relative paths to absolute. diff --git a/src/commands.rs b/src/commands.rs index 6d99a7715..f58753a19 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -12,17 +12,14 @@ use utils::colors; use crate::{ archive, - cli::{Opts, Subcommand}, error::FinalError, extension::{ self, CompressionFormat::{self, *}, }, info, - utils::nice_directory_display, - utils::to_utf, - utils::{self, dir_is_empty, QuestionPolicy}, - Error, + utils::{self, dir_is_empty, nice_directory_display, to_utf}, + Error, Opts, QuestionPolicy, Subcommand, }; // Used in BufReader and BufWriter to perform less syscalls diff --git a/src/lib.rs b/src/lib.rs index 9a1a795a6..c6f5a071a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,18 +5,21 @@ //! 2. It's required by some integration tests at tests/ folder. // Public modules -pub mod cli; +pub mod archive; pub mod commands; // Private modules -pub mod archive; +mod cli; mod dialogs; mod error; mod extension; mod macros; +mod opts; mod utils; pub use error::{Error, Result}; +pub use opts::{Opts, Subcommand}; +pub use utils::QuestionPolicy; /// The status code ouch has when an error is encountered pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE; diff --git a/src/main.rs b/src/main.rs index 9fc88ec8b..eb886f95b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use ouch::{cli::Opts, commands, Result}; +use ouch::{commands, Opts, Result}; fn main() { if let Err(err) = run() { diff --git a/src/opts.rs b/src/opts.rs new file mode 100644 index 000000000..6d414a81b --- /dev/null +++ b/src/opts.rs @@ -0,0 +1,44 @@ +use clap::{Parser, ValueHint}; + +use std::path::PathBuf; + +#[derive(Parser, Debug)] +#[clap(version, about)] +pub struct Opts { + /// Skip overwrite questions positively. + #[clap(short, long, conflicts_with = "no")] + pub yes: bool, + + /// Skip overwrite questions negatively. + #[clap(short, long)] + pub no: bool, + + #[clap(subcommand)] + pub cmd: Subcommand, +} + +#[derive(Parser, PartialEq, Eq, Debug)] +pub enum Subcommand { + /// Compress files. Alias: c + #[clap(alias = "c")] + Compress { + /// Files to be compressed + #[clap(required = true, min_values = 1)] + files: Vec, + + /// The resulting file. Its extensions specify how the files will be compressed and they need to be supported + #[clap(required = true, value_hint = ValueHint::FilePath)] + output: PathBuf, + }, + /// Compress files. Alias: d + #[clap(alias = "d")] + Decompress { + /// Files to be decompressed + #[clap(required = true, min_values = 1)] + files: Vec, + + /// Decompress files in a directory other than the current + #[clap(short, long, value_hint = ValueHint::DirPath)] + output: Option, + }, +} diff --git a/tests/compress_and_decompress.rs b/tests/compress_and_decompress.rs index f78a5aaf2..2b6efa1d9 100644 --- a/tests/compress_and_decompress.rs +++ b/tests/compress_and_decompress.rs @@ -7,12 +7,9 @@ use std::{ time::Duration, }; -use fs_err as fs; +use ouch::{commands::run, Opts, QuestionPolicy, Subcommand}; -use ouch::{ - cli::{Opts, QuestionPolicy, Subcommand}, - commands::run, -}; +use fs_err as fs; use rand::{rngs::SmallRng, RngCore, SeedableRng}; use tempfile::NamedTempFile; use utils::*; diff --git a/tests/utils.rs b/tests/utils.rs index 0d0fdd068..e085ec0e0 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -6,10 +6,7 @@ use std::path::{Path, PathBuf}; use fs_err as fs; -use ouch::{ - cli::{Opts, QuestionPolicy, Subcommand}, - commands::run, -}; +use ouch::{commands::run, Opts, QuestionPolicy, Subcommand}; pub fn create_empty_dir(at: &Path, filename: &str) -> PathBuf { let dirname = Path::new(filename);