summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2022-06-17 14:57:06 -0700
committerMichael Howell <michael@notriddle.com>2022-06-17 14:57:06 -0700
commitb3a4b38494e2861120a5e3708f2e060de16489df (patch)
tree9440f2448619885b2b073a4d3247e2d7a1cf6525
parent5254dbfd25d5284728ab624dca1969d61427a0db (diff)
downloadrust-installer-b3a4b38494e2861120a5e3708f2e060de16489df.tar.gz
Bump to clap 3
-rw-r--r--Cargo.toml4
-rw-r--r--src/combiner.rs12
-rw-r--r--src/compression.rs25
-rw-r--r--src/generator.rs14
-rw-r--r--src/main.rs106
-rw-r--r--src/main.yml172
-rw-r--r--src/scripter.rs5
-rw-r--r--src/tarballer.rs6
-rw-r--r--src/util.rs21
9 files changed, 92 insertions, 273 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 7979528..4a4da7e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,8 +20,8 @@ num_cpus = "1"
remove_dir_all = "0.5"
[dependencies.clap]
-features = ["yaml"]
-version = "2.19.0"
+features = ["derive"]
+version = "3.1"
[target."cfg(windows)".dependencies]
lazy_static = "1"
diff --git a/src/combiner.rs b/src/combiner.rs
index 006a40c..2ec09d6 100644
--- a/src/combiner.rs
+++ b/src/combiner.rs
@@ -13,34 +13,44 @@ actor! {
#[derive(Debug)]
pub struct Combiner {
/// The name of the product, for display.
+ #[clap(value_name = "NAME")]
product_name: String = "Product",
/// The name of the package tarball.
+ #[clap(value_name = "NAME")]
package_name: String = "package",
/// The directory under lib/ where the manifest lives.
+ #[clap(value_name = "DIR")]
rel_manifest_dir: String = "packagelib",
/// The string to print after successful installation.
+ #[clap(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall.
+ #[clap(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// Installers to combine.
+ #[clap(value_name = "FILE,FILE")]
input_tarballs: String = "",
/// Directory containing files that should not be installed.
+ #[clap(value_name = "DIR")]
non_installed_overlay: String = "",
/// The directory to do temporary work.
+ #[clap(value_name = "DIR")]
work_dir: String = "./workdir",
/// The location to put the final image and tarball.
+ #[clap(value_name = "DIR")]
output_dir: String = "./dist",
/// The formats used to compress the tarball
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ #[clap(value_name = "FORMAT", default_value_t)]
+ compression_formats: CompressionFormats,
}
}
diff --git a/src/compression.rs b/src/compression.rs
index b3010cb..7e20a94 100644
--- a/src/compression.rs
+++ b/src/compression.rs
@@ -1,7 +1,7 @@
use anyhow::{Context, Error};
use flate2::{read::GzDecoder, write::GzEncoder};
use rayon::prelude::*;
-use std::{convert::TryFrom, io::Read, io::Write, path::Path};
+use std::{convert::TryFrom, fmt, io::Read, io::Write, path::Path, str::FromStr};
use xz2::{read::XzDecoder, write::XzEncoder};
#[derive(Debug, Copy, Clone)]
@@ -80,6 +80,29 @@ impl TryFrom<&'_ str> for CompressionFormats {
}
}
+impl FromStr for CompressionFormats {
+ type Err = Error;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ Self::try_from(value)
+ }
+}
+
+impl fmt::Display for CompressionFormats {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for (i, format) in self.iter().enumerate() {
+ if i != 0 {
+ write!(f, ",")?;
+ }
+ fmt::Display::fmt(match format {
+ CompressionFormat::Xz => "xz",
+ CompressionFormat::Gz => "gz",
+ }, f)?;
+ }
+ Ok(())
+ }
+}
+
impl Default for CompressionFormats {
fn default() -> Self {
Self(vec![CompressionFormat::Gz, CompressionFormat::Xz])
diff --git a/src/generator.rs b/src/generator.rs
index 2601eb5..6a4cb9b 100644
--- a/src/generator.rs
+++ b/src/generator.rs
@@ -10,40 +10,52 @@ actor! {
#[derive(Debug)]
pub struct Generator {
/// The name of the product, for display
+ #[clap(value_name = "NAME")]
product_name: String = "Product",
/// The name of the component, distinct from other installed components
+ #[clap(value_name = "NAME")]
component_name: String = "component",
/// The name of the package, tarball
+ #[clap(value_name = "NAME")]
package_name: String = "package",
/// The directory under lib/ where the manifest lives
+ #[clap(value_name = "DIR")]
rel_manifest_dir: String = "packagelib",
/// The string to print after successful installation
+ #[clap(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall
+ #[clap(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// Directory containing files that should not be installed
+ #[clap(value_name = "DIR")]
non_installed_overlay: String = "",
/// Path prefixes of directories that should be installed/uninstalled in bulk
+ #[clap(value_name = "DIRS")]
bulk_dirs: String = "",
/// The directory containing the installation medium
+ #[clap(value_name = "DIR")]
image_dir: String = "./install_image",
/// The directory to do temporary work
+ #[clap(value_name = "DIR")]
work_dir: String = "./workdir",
/// The location to put the final image and tarball
+ #[clap(value_name = "DIR")]
output_dir: String = "./dist",
/// The formats used to compress the tarball
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ #[clap(value_name = "FORMAT", default_value_t)]
+ compression_formats: CompressionFormats,
}
}
diff --git a/src/main.rs b/src/main.rs
index e933dd0..823d269 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,96 +1,28 @@
use anyhow::{Context, Result};
-use clap::{App, ArgMatches};
+use clap::{self, Command, Parser};
use std::convert::TryInto;
-fn main() -> Result<()> {
- let yaml = clap::load_yaml!("main.yml");
- let matches = App::from_yaml(yaml).get_matches();
-
- match matches.subcommand() {
- ("combine", Some(matches)) => combine(matches),
- ("generate", Some(matches)) => generate(matches),
- ("script", Some(matches)) => script(matches),
- ("tarball", Some(matches)) => tarball(matches),
- _ => unreachable!(),
- }
-}
-
-/// Parse clap arguements into the type constructor.
-macro_rules! parse(
- ($matches:expr => $type:ty { $( $option:tt => $setter:ident, )* }) => {
- {
- let mut command: $type = Default::default();
- $(
- if let Some(val) = $matches.value_of($option) {
- command.$setter(val.try_into()?);
- }
- )*
- command
- }
- }
-);
-
-fn combine(matches: &ArgMatches<'_>) -> Result<()> {
- let combiner = parse!(matches => installer::Combiner {
- "product-name" => product_name,
- "package-name" => package_name,
- "rel-manifest-dir" => rel_manifest_dir,
- "success-message" => success_message,
- "legacy-manifest-dirs" => legacy_manifest_dirs,
- "input-tarballs" => input_tarballs,
- "non-installed-overlay" => non_installed_overlay,
- "work-dir" => work_dir,
- "output-dir" => output_dir,
- "compression-formats" => compression_formats,
- });
-
- combiner.run().context("failed to combine installers")?;
- Ok(())
+#[derive(Parser)]
+struct CommandLine {
+ #[clap(subcommand)]
+ command: Subcommand,
}
-fn generate(matches: &ArgMatches<'_>) -> Result<()> {
- let generator = parse!(matches => installer::Generator {
- "product-name" => product_name,
- "component-name" => component_name,
- "package-name" => package_name,
- "rel-manifest-dir" => rel_manifest_dir,
- "success-message" => success_message,
- "legacy-manifest-dirs" => legacy_manifest_dirs,
- "non-installed-overlay" => non_installed_overlay,
- "bulk-dirs" => bulk_dirs,
- "image-dir" => image_dir,
- "work-dir" => work_dir,
- "output-dir" => output_dir,
- "compression-formats" => compression_formats,
- });
-
- generator.run().context("failed to generate installer")?;
- Ok(())
+#[derive(clap::Subcommand)]
+enum Subcommand {
+ Generate(installer::Generator),
+ Combine(installer::Combiner),
+ Script(installer::Scripter),
+ Tarball(installer::Tarballer),
}
-fn script(matches: &ArgMatches<'_>) -> Result<()> {
- let scripter = parse!(matches => installer::Scripter {
- "product-name" => product_name,
- "rel-manifest-dir" => rel_manifest_dir,
- "success-message" => success_message,
- "legacy-manifest-dirs" => legacy_manifest_dirs,
- "output-script" => output_script,
- });
-
- scripter
- .run()
- .context("failed to generate installation script")?;
- Ok(())
-}
-
-fn tarball(matches: &ArgMatches<'_>) -> Result<()> {
- let tarballer = parse!(matches => installer::Tarballer {
- "input" => input,
- "output" => output,
- "work-dir" => work_dir,
- "compression-formats" => compression_formats,
- });
-
- tarballer.run().context("failed to generate tarballs")?;
+fn main() -> Result<()> {
+ let command_line = CommandLine::parse();
+ match command_line.command {
+ Subcommand::Combine(combiner) => combiner.run().context("failed to combine installers")?,
+ Subcommand::Generate(generator) => generator.run().context("failed to generate installer")?,
+ Subcommand::Script(scripter) => scripter.run().context("failed to generate installation script")?,
+ Subcommand::Tarball(tarballer) => tarballer.run().context("failed to generate tarballs")?,
+ }
Ok(())
}
diff --git a/src/main.yml b/src/main.yml
deleted file mode 100644
index 7b6d735..0000000
--- a/src/main.yml
+++ /dev/null
@@ -1,172 +0,0 @@
-name: installer
-settings:
- - ArgRequiredElseHelp
-subcommands:
- - generate:
- about: Generate a complete installer tarball
- args:
- - product-name:
- help: The name of the product, for display
- long: product-name
- takes_value: true
- value_name: NAME
- - component-name:
- help: The name of the component, distinct from other installed components
- long: component-name
- takes_value: true
- value_name: NAME
- - package-name:
- help: The name of the package, tarball
- long: package-name
- takes_value: true
- value_name: NAME
- - rel-manifest-dir:
- help: The directory under lib/ where the manifest lives
- long: rel-manifest-dir
- takes_value: true
- value_name: DIR
- - success-message:
- help: The string to print after successful installation
- long: success-message
- takes_value: true
- value_name: MESSAGE
- - legacy-manifest-dirs:
- help: Places to look for legacy manifests to uninstall
- long: legacy-manifest-dirs
- takes_value: true
- value_name: DIRS
- - non-installed-overlay:
- help: Directory containing files that should not be installed
- long: non-installed-overlay
- takes_value: true
- value_name: DIR
- - bulk-dirs:
- help: Path prefixes of directories that should be installed/uninstalled in bulk
- long: bulk-dirs
- takes_value: true
- value_name: DIRS
- - image-dir:
- help: The directory containing the installation medium
- long: image-dir
- takes_value: true
- value_name: DIR
- - work-dir:
- help: The directory to do temporary work
- long: work-dir
- takes_value: true
- value_name: DIR
- - output-dir:
- help: The location to put the final image and tarball
- long: output-dir
- takes_value: true
- value_name: DIR
- - compression-formats:
- help: Comma-separated list of compression formats to use
- long: compression-formats
- takes_value: true
- value_name: FORMAT
- - combine:
- about: Combine installer tarballs
- args:
- - product-name:
- help: The name of the product, for display
- long: product-name
- takes_value: true
- value_name: NAME
- - package-name:
- help: The name of the package, tarball
- long: package-name
- takes_value: true
- value_name: NAME
- - rel-manifest-dir:
- help: The directory under lib/ where the manifest lives
- long: rel-manifest-dir
- takes_value: true
- value_name: DIR
- - success-message:
- help: The string to print after successful installation
- long: success-message
- takes_value: true
- value_name: MESSAGE
- - legacy-manifest-dirs:
- help: Places to look for legacy manifests to uninstall
- long: legacy-manifest-dirs
- takes_value: true
- value_name: DIRS
- - input-tarballs:
- help: Installers to combine
- long: input-tarballs
- takes_value: true
- value_name: FILE,FILE
- - non-installed-overlay:
- help: Directory containing files that should not be installed
- long: non-installed-overlay
- takes_value: true
- value_name: DIR
- - work-dir:
- help: The directory to do temporary work
- long: work-dir
- takes_value: true
- value_name: DIR
- - output-dir:
- help: The location to put the final image and tarball
- long: output-dir
- takes_value: true
- value_name: DIR
- - compression-formats:
- help: Comma-separated list of compression formats to use
- long: compression-formats
- takes_value: true
- value_name: FORMAT
- - script:
- about: Generate an installation script
- args:
- - product-name:
- help: The name of the product, for display
- long: product-name
- takes_value: true
- value_name: NAME
- - rel-manifest-dir:
- help: The directory under lib/ where the manifest lives
- long: rel-manifest-dir
- takes_value: true
- value_name: DIR
- - success-message:
- help: The string to print after successful installation
- long: success-message
- takes_value: true
- value_name: MESSAGE
- - legacy-manifest-dirs:
- help: Places to look for legacy manifests to uninstall
- long: legacy-manifest-dirs
- takes_value: true
- value_name: DIRS
- - output-script:
- help: The name of the output script
- long: output-script
- takes_value: true
- value_name: FILE
- - tarball:
- about: Generate package tarballs
- args:
- - input:
- help: The input folder to be compressed
- long: input
- takes_value: true
- value_name: NAME
- - output:
- help: The prefix of the tarballs
- long: output
- takes_value: true
- value_name: PATH
- - work-dir:
- help: The folder in which the input is to be found
- long: work-dir
- takes_value: true
- value_name: DIR
- - compression-formats:
- help: Comma-separated list of compression formats to use
- long: compression-formats
- takes_value: true
- value_name: FORMAT
-
diff --git a/src/scripter.rs b/src/scripter.rs
index 9e82ae7..06affc0 100644
--- a/src/scripter.rs
+++ b/src/scripter.rs
@@ -8,18 +8,23 @@ actor! {
#[derive(Debug)]
pub struct Scripter {
/// The name of the product, for display
+ #[clap(value_name = "NAME")]
product_name: String = "Product",
/// The directory under lib/ where the manifest lives
+ #[clap(value_name = "DIR")]
rel_manifest_dir: String = "manifestlib",
/// The string to print after successful installation
+ #[clap(value_name = "MESSAGE")]
success_message: String = "Installed.",
/// Places to look for legacy manifests to uninstall
+ #[clap(value_name = "DIRS")]
legacy_manifest_dirs: String = "",
/// The name of the output script
+ #[clap(value_name = "FILE")]
output_script: String = "install.sh",
}
}
diff --git a/src/tarballer.rs b/src/tarballer.rs
index 4ac8cf7..76f5af3 100644
--- a/src/tarballer.rs
+++ b/src/tarballer.rs
@@ -14,16 +14,20 @@ actor! {
#[derive(Debug)]
pub struct Tarballer {
/// The input folder to be compressed.
+ #[clap(value_name = "NAME")]
input: String = "package",
/// The prefix of the tarballs.
+ #[clap(value_name = "PATH")]
output: String = "./dist",
/// The folder in which the input is to be found.
+ #[clap(value_name = "DIR")]
work_dir: String = "./workdir",
/// The formats used to compress the tarball.
- compression_formats: CompressionFormats = CompressionFormats::default(),
+ #[clap(value_name = "FORMAT", default_value_t)]
+ compression_formats: CompressionFormats,
}
}
diff --git a/src/util.rs b/src/util.rs
index 078ceb3..674617c 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -122,30 +122,35 @@ where
Ok(())
}
-/// Creates an "actor" with default values and setters for all fields.
+macro_rules! actor_field_default {
+ () => { Default::default() };
+ (= $expr:expr) => { $expr.into() }
+}
+
+/// Creates an "actor" with default values, setters for all fields, and Clap parser support.
macro_rules! actor {
($( #[ $attr:meta ] )+ pub struct $name:ident {
- $( $( #[ $field_attr:meta ] )+ $field:ident : $type:ty = $default:expr, )*
+ $( $( #[ $field_attr:meta ] )+ $field:ident : $type:ty $(= $default:tt)*, )*
}) => {
$( #[ $attr ] )+
+ #[derive(clap::Args)]
pub struct $name {
- $( $( #[ $field_attr ] )+ $field : $type, )*
+ $( $( #[ $field_attr ] )+ #[clap(long, $(default_value = $default)*)] $field : $type, )*
}
impl Default for $name {
- fn default() -> Self {
+ fn default() -> $name {
$name {
- $( $field : $default.into(), )*
+ $($field : actor_field_default!($(= $default)*), )*
}
}
}
impl $name {
- $( $( #[ $field_attr ] )+
- pub fn $field(&mut self, value: $type) -> &mut Self {
+ $(pub fn $field(&mut self, value: $type) -> &mut Self {
self.$field = value;
self
- })+
+ })*
}
}
}