Simplify subcommand parsing:
- one match statement to return a `Box<dyn Cmd>` - one array containing all command names Only two places to register new commands (besides their module), both in crate::cmd::mod.rs. Also removes `once_cell` crate dependency. Replace `base64` crate dependency with `data_encoding::BASE64` so that both base32 and base64 commands use the same crate.
This commit is contained in:
parent
84ede35190
commit
fb389fd309
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -13,12 +13,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -178,12 +172,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
@ -215,7 +203,6 @@ name = "shitbox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"base64",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_complete_nushell",
|
||||
@ -223,7 +210,6 @@ dependencies = [
|
||||
"data-encoding",
|
||||
"hostname",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
|
@ -7,7 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.14"
|
||||
base64 = "0.20.0"
|
||||
clap = "4.0.29"
|
||||
clap_complete = "4.0.6"
|
||||
clap_complete_nushell = "0.1.8"
|
||||
@ -15,7 +14,6 @@ clap_mangen = "0.2.5"
|
||||
data-encoding = "2.3.3"
|
||||
hostname = { version = "0.3", features = ["set"] }
|
||||
libc = "0.2.139"
|
||||
once_cell = "1.16.0"
|
||||
termcolor = "1.1.3"
|
||||
|
||||
[profile.release]
|
||||
|
BIN
pkg/bin/shitbox
BIN
pkg/bin/shitbox
Binary file not shown.
@ -1,38 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH base32 1 "base32 0.1.0"
|
||||
.SH NAME
|
||||
base32 \- Base32 encode/decode data and print to standard output
|
||||
.SH SYNOPSIS
|
||||
\fBbase32\fR [\fB\-d\fR|\fB\-\-decode\fR] [\fB\-i\fR|\fB\-\-ignore\-space\fR] [\fB\-w\fR|\fB\-\-wrap\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIINPUT\fR]
|
||||
.SH DESCRIPTION
|
||||
Base32 encode/decode data and print to standard output
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-decode\fR
|
||||
Decode rather than encode
|
||||
.TP
|
||||
\fB\-i\fR, \fB\-\-ignore\-space\fR
|
||||
Ignore whitespace when decoding
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-wrap\fR [default: 76]
|
||||
Wrap encoded lines after n characters
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Display a header naming each file
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
Do not display header, even with multiple files
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
[\fIINPUT\fR]
|
||||
The input file to use
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
The JeanG3nie <jeang3nie@hitchhiker\-linux.org>
|
@ -1,24 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH dirname 1 "dirname 0.1.0"
|
||||
.SH NAME
|
||||
dirname \- strip last component from file name
|
||||
.SH SYNOPSIS
|
||||
\fBdirname\fR [\fB\-z\fR|\fB\-\-zero\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] <\fIname\fR>
|
||||
.SH DESCRIPTION
|
||||
strip last component from file name
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-z\fR, \fB\-\-zero\fR
|
||||
end each output line with NUL, not newline
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
<\fIname\fR>
|
||||
|
||||
.SH VERSION
|
||||
v0.1.0
|
@ -1,26 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH echo 1 "echo 0.1.0"
|
||||
.SH NAME
|
||||
echo \- Display a line of text
|
||||
.SH SYNOPSIS
|
||||
\fBecho\fR [\fB\-n \fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fISTRING\fR]
|
||||
.SH DESCRIPTION
|
||||
Echo the STRING(s) to standard output
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-n\fR
|
||||
Do not output a trailing newline
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
[\fISTRING\fR]
|
||||
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -1,20 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH false 1 "false 0.1.0"
|
||||
.SH NAME
|
||||
false \- Does nothing unsuccessfully
|
||||
.SH SYNOPSIS
|
||||
\fBfalse\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR]
|
||||
.SH DESCRIPTION
|
||||
Exit with a status code indicating failure
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -1,38 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH head 1 "head 0.1.0"
|
||||
.SH NAME
|
||||
head \- Display first lines of a file
|
||||
.SH SYNOPSIS
|
||||
\fBhead\fR [\fB\-c\fR|\fB\-\-bytes\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-v\fR|\fB\-\-verbose\fR] [\fB\-n\fR|\fB\-\-lines\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIFILES\fR]
|
||||
.SH DESCRIPTION
|
||||
Print the first 10 lines of each FILE to standard output.
|
||||
With more than one FILE, precede each with a header giving the file name.
|
||||
.PP
|
||||
With no FILE, or when FILE is \-, read standard input.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-bytes\fR
|
||||
Count bytes instead of lines
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
Disable printing a header. Overrides \-c
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Each file is preceded by a header consisting of the string "==> XXX <==" where "XXX" is the name of the file.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-lines\fR
|
||||
Count n number of lines (or bytes if \-c is specified).
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
[\fIFILES\fR]
|
||||
The input file to use
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -1,26 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH hostname 1 "hostname 0.1.0"
|
||||
.SH NAME
|
||||
hostname \- Prints the name of the current host. The super\-user can set the host name by supplying an argument.
|
||||
.SH SYNOPSIS
|
||||
\fBhostname\fR [\fB\-s\fR|\fB\-\-strip\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fINAME\fR]
|
||||
.SH DESCRIPTION
|
||||
Prints the name of the current host. The super\-user can set the host name by supplying an argument.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-strip\fR
|
||||
Removes any domain information from the printed name.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
[\fINAME\fR]
|
||||
name to set
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
The JeanG3nie <jeang3nie@hitchhiker\-linux.org>
|
@ -1,20 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH nologin 1 "nologin 0.1.0"
|
||||
.SH NAME
|
||||
nologin \- Denies a user account login ability
|
||||
.SH SYNOPSIS
|
||||
\fBnologin\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR]
|
||||
.SH DESCRIPTION
|
||||
Denies a user account login ability
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -1,46 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH bootstrap 1 "bootstrap 0.1.0"
|
||||
.SH NAME
|
||||
bootstrap \- Install shitbox into the filesystem
|
||||
.SH SYNOPSIS
|
||||
\fBbootstrap\fR [\fB\-p\fR|\fB\-\-prefix\fR] [\fB\-u\fR|\fB\-\-usr\fR] [\fB\-s\fR|\fB\-\-soft\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIsubcommands\fR]
|
||||
.SH DESCRIPTION
|
||||
Install symlinks, manpages and shell completions
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-prefix\fR [default: /]
|
||||
The directory path under which to install
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-usr\fR
|
||||
Split the installation so that some applets go into /bin | /sbin
|
||||
while others are placed into /usr/bin | /usr/sbin
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-soft\fR
|
||||
Install soft links instead of hardlinks
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.SH SUBCOMMANDS
|
||||
.TP
|
||||
bootstrap\-all(1)
|
||||
Install everything
|
||||
.TP
|
||||
bootstrap\-links(1)
|
||||
Install links for each applet
|
||||
.TP
|
||||
bootstrap\-manpages(1)
|
||||
Install Unix man pages
|
||||
.TP
|
||||
bootstrap\-completions(1)
|
||||
Install shell completions
|
||||
.TP
|
||||
bootstrap\-help(1)
|
||||
Print this message or the help of the given subcommand(s)
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -1,52 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH shitbox 1 "shitbox 0.1.0"
|
||||
.SH NAME
|
||||
shitbox \- The Harbor Freight multitool of embedded Linux
|
||||
.SH SYNOPSIS
|
||||
\fBshitbox\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIsubcommands\fR]
|
||||
.SH DESCRIPTION
|
||||
The Harbor Freight multitool of embedded Linux
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.SH SUBCOMMANDS
|
||||
.TP
|
||||
shitbox\-base32(1)
|
||||
Base32 encode/decode data and print to standard output
|
||||
.TP
|
||||
shitbox\-bootstrap(1)
|
||||
Install shitbox into the filesystem
|
||||
.TP
|
||||
shitbox\-echo(1)
|
||||
Display a line of text
|
||||
.TP
|
||||
shitbox\-dirname(1)
|
||||
strip last component from file name
|
||||
.TP
|
||||
shitbox\-false(1)
|
||||
Does nothing unsuccessfully
|
||||
.TP
|
||||
shitbox\-head(1)
|
||||
Display first lines of a file
|
||||
.TP
|
||||
shitbox\-nologin(1)
|
||||
Denies a user account login ability
|
||||
.TP
|
||||
shitbox\-hostname(1)
|
||||
Prints the name of the current host. The super\-user can set the host name by supplying an argument.
|
||||
.TP
|
||||
shitbox\-sleep(1)
|
||||
Suspend execution for an interval of time
|
||||
.TP
|
||||
shitbox\-true(1)
|
||||
Does nothing successfully
|
||||
.TP
|
||||
shitbox\-help(1)
|
||||
Print this message or the help of the given subcommand(s)
|
||||
.SH VERSION
|
||||
v0.1.0
|
@ -1,25 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH sleep 1 "sleep 0.1.0"
|
||||
.SH NAME
|
||||
sleep \- Suspend execution for an interval of time
|
||||
.SH SYNOPSIS
|
||||
\fBsleep\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] <\fIseconds\fR>
|
||||
.SH DESCRIPTION
|
||||
The sleep utility suspends execution for a minimum of the specified number of seconds.
|
||||
This number must be positive and may contain a decimal fraction.
|
||||
sleep is commonly used to schedule the execution of other commands
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.TP
|
||||
<\fIseconds\fR>
|
||||
The number of seconds to sleep
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
|
@ -1,20 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH true 1 "true 0.1.0"
|
||||
.SH NAME
|
||||
true \- Does nothing successfully
|
||||
.SH SYNOPSIS
|
||||
\fBtrue\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR]
|
||||
.SH DESCRIPTION
|
||||
Exit with a status code indicating success
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help information (use `\-h` for a summary)
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version information
|
||||
.SH VERSION
|
||||
v0.1.0
|
||||
.SH AUTHORS
|
||||
Nathan Fisher
|
@ -14,10 +14,14 @@ pub struct Base32 {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const BASE_32: Base32 = Base32 {
|
||||
name: "base32",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
};
|
||||
impl Default for Base32 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "base32",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Base32 {
|
||||
fn name(&self) -> &str {
|
||||
@ -129,7 +133,7 @@ fn decode_base32(mut contents: String, ignore: bool) -> Result<(), Box<dyn Error
|
||||
}
|
||||
let decoded = BASE32.decode(contents.as_bytes())?;
|
||||
let output = String::from_utf8(decoded)?;
|
||||
println!("{}", output.trim_end());
|
||||
println!("{}\n", output.trim_end());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::Cmd;
|
||||
use base64::{decode, encode};
|
||||
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use data_encoding::BASE64;
|
||||
use std::{
|
||||
error::Error,
|
||||
fs,
|
||||
@ -14,10 +14,14 @@ pub struct Base64 {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const BASE_64: Base64 = Base64 {
|
||||
name: "base64",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
};
|
||||
impl Default for Base64 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "base64",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Base64 {
|
||||
fn name(&self) -> &str {
|
||||
@ -125,14 +129,15 @@ fn decode_base64(mut contents: String, ignore: bool) -> Result<(), Box<dyn Error
|
||||
} else {
|
||||
contents = contents.replace('\n', "");
|
||||
}
|
||||
let decoded = decode(&contents)?.clone();
|
||||
let decoded = BASE64.decode(&contents.as_bytes())?;
|
||||
let output = String::from_utf8(decoded)?;
|
||||
println!("{}", output.trim_end());
|
||||
println!("{}\n", output.trim_end());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encode_base64(contents: &str, wrap: usize) {
|
||||
encode(contents.as_bytes())
|
||||
BASE64
|
||||
.encode(contents.as_bytes())
|
||||
.chars()
|
||||
.collect::<Vec<char>>()
|
||||
.chunks(wrap)
|
||||
|
@ -1,12 +1,14 @@
|
||||
use super::{Cmd, Commands};
|
||||
use super::{Cmd, COMMANDS};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use clap_complete::shells;
|
||||
use clap_complete::{generate_to, shells};
|
||||
use clap_complete_nushell::Nushell;
|
||||
use clap_mangen::Man;
|
||||
use std::{
|
||||
error::Error,
|
||||
fs,
|
||||
io::{self, ErrorKind},
|
||||
path::PathBuf,
|
||||
os::unix::fs::symlink,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -15,10 +17,14 @@ pub struct Bootstrap {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const BOOTSTRAP: Bootstrap = Bootstrap {
|
||||
name: "bootstrap",
|
||||
path: None,
|
||||
};
|
||||
impl Default for Bootstrap {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "bootstrap",
|
||||
path: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bootstrap {
|
||||
fn all() -> clap::Command {
|
||||
@ -167,9 +173,6 @@ impl Cmd for Bootstrap {
|
||||
return Err(io::Error::new(ErrorKind::Other, "No input").into());
|
||||
};
|
||||
if let Some(prefix) = matches.get_one::<String>("prefix") {
|
||||
let commands = super::COMMANDS
|
||||
.get()
|
||||
.ok_or_else(|| io::Error::new(ErrorKind::Other, "Cannot get commands list"))?;
|
||||
let usr = matches.get_flag("usr");
|
||||
if let Some(progpath) = crate::progpath() {
|
||||
let mut outpath = PathBuf::from(prefix);
|
||||
@ -189,18 +192,18 @@ impl Cmd for Bootstrap {
|
||||
}
|
||||
match matches.subcommand() {
|
||||
Some(("links", matches)) => {
|
||||
commands.links(prefix, usr, matches)?;
|
||||
links(prefix, usr, matches)?;
|
||||
}
|
||||
Some(("manpages", _matches)) => {
|
||||
commands.manpages(prefix)?;
|
||||
manpages(prefix)?;
|
||||
}
|
||||
Some(("completions", matches)) => {
|
||||
commands.completions(prefix, matches)?;
|
||||
completions(prefix, matches)?;
|
||||
}
|
||||
Some(("all", matches)) => {
|
||||
commands.links(prefix, usr, matches)?;
|
||||
commands.manpages(prefix)?;
|
||||
commands.completions(prefix, matches)?;
|
||||
links(prefix, usr, matches)?;
|
||||
manpages(prefix)?;
|
||||
completions(prefix, matches)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -213,59 +216,178 @@ impl Cmd for Bootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Bootstrappable {
|
||||
fn manpages(&self, prefix: &str) -> Result<(), io::Error>;
|
||||
fn completions(&self, prefix: &str, matches: &ArgMatches) -> Result<(), io::Error>;
|
||||
fn links(&self, prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box<dyn Error>>;
|
||||
pub trait BootstrapCmd {
|
||||
fn completion(&self, outdir: &Path, gen: &str) -> Result<(), io::Error>;
|
||||
fn linkpath(&self, prefix: &str, usr: bool) -> Option<PathBuf>;
|
||||
fn link(&self, prefix: &str, usr: bool, soft: bool) -> Result<(), Box<dyn Error>>;
|
||||
fn manpage(&self, prefix: &str) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
impl<'a> Bootstrappable for Commands<'a> {
|
||||
fn manpages(&self, prefix: &str) -> Result<(), io::Error> {
|
||||
println!("Generating Unix man pages:");
|
||||
self.items.iter().try_for_each(|cmd| cmd.manpage(prefix))?;
|
||||
impl BootstrapCmd for dyn Cmd {
|
||||
fn completion(&self, outdir: &Path, gen: &str) -> Result<(), io::Error> {
|
||||
let name = self.name();
|
||||
let mut cmd = self.cli();
|
||||
if !outdir.exists() {
|
||||
fs::create_dir_all(outdir)?;
|
||||
}
|
||||
let path = match gen {
|
||||
"bash" => generate_to(shells::Bash, &mut cmd, name, outdir)?,
|
||||
"fish" => generate_to(shells::Fish, &mut cmd, name, outdir)?,
|
||||
"nu" => generate_to(Nushell, &mut cmd, name, outdir)?,
|
||||
"pwsh" => generate_to(shells::PowerShell, &mut cmd, name, outdir)?,
|
||||
"zsh" => generate_to(shells::Zsh, &mut cmd, name, outdir)?,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
println!(" {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn completions(&self, prefix: &str, matches: &ArgMatches) -> Result<(), io::Error> {
|
||||
println!("Generating completions:");
|
||||
if matches.get_flag("bash") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "bash-completion", "completion"]
|
||||
.iter()
|
||||
.collect();
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| Self::completion(&outdir, cmd, shells::Bash))?;
|
||||
fn linkpath(&self, prefix: &str, usr: bool) -> Option<PathBuf> {
|
||||
let mut path = PathBuf::from(prefix);
|
||||
let binpath = self.path();
|
||||
match binpath {
|
||||
Some(crate::Path::Bin) => path.push("bin"),
|
||||
Some(crate::Path::Sbin) => path.push("sbin"),
|
||||
Some(crate::Path::UsrBin) => {
|
||||
if usr {
|
||||
path.push("usr");
|
||||
}
|
||||
path.push("bin");
|
||||
}
|
||||
Some(crate::Path::UsrSbin) => {
|
||||
if usr {
|
||||
path.push("usr");
|
||||
}
|
||||
path.push("sbin");
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
if matches.get_flag("fish") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "fish", "completions"].iter().collect();
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| Self::completion(&outdir, cmd, shells::Fish))?;
|
||||
}
|
||||
if matches.get_flag("nu") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "nu", "completions"].iter().collect();
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| Self::completion(&outdir, cmd, Nushell))?;
|
||||
}
|
||||
if matches.get_flag("pwsh") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "pwsh", "completions"].iter().collect();
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| Self::completion(&outdir, cmd, shells::PowerShell))?;
|
||||
}
|
||||
if matches.get_flag("zsh") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "zsh", "site-functions"].iter().collect();
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| Self::completion(&outdir, cmd, shells::Zsh))?;
|
||||
path.push(self.name());
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn link(&self, prefix: &str, usr: bool, soft: bool) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(linkpath) = self.linkpath(prefix, usr) {
|
||||
if soft {
|
||||
let binpath = match self.path().unwrap() {
|
||||
crate::Path::Bin => "shitbox",
|
||||
crate::Path::Sbin => "../bin/shitbox",
|
||||
crate::Path::UsrBin => {
|
||||
if usr {
|
||||
"../../bin/shitbox"
|
||||
} else {
|
||||
"shitbox"
|
||||
}
|
||||
}
|
||||
crate::Path::UsrSbin => {
|
||||
if usr {
|
||||
"../../bin/shitbox"
|
||||
} else {
|
||||
"../bin/shitbox"
|
||||
}
|
||||
}
|
||||
};
|
||||
symlink(binpath, &linkpath)?;
|
||||
println!(" symlink: {binpath} -> {}", linkpath.display());
|
||||
} else {
|
||||
let mut binpath = PathBuf::from(prefix);
|
||||
binpath.push("bin");
|
||||
binpath.push(env!("CARGO_PKG_NAME"));
|
||||
fs::hard_link(&binpath, &linkpath)?;
|
||||
println!(" link: {} -> {}", binpath.display(), linkpath.display());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn links(&self, prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||
println!("Generating links:");
|
||||
let mut binpath = PathBuf::from(prefix);
|
||||
fn manpage(&self, prefix: &str) -> Result<(), io::Error> {
|
||||
let command = self.cli();
|
||||
let fname = match self.name() {
|
||||
"bootstrap" => "shitbox-bootstrap.1".to_string(),
|
||||
s => format!("{s}.1"),
|
||||
};
|
||||
let outdir: PathBuf = [prefix, "usr", "share", "man", "man1"].iter().collect();
|
||||
if !outdir.exists() {
|
||||
fs::create_dir_all(&outdir)?;
|
||||
}
|
||||
let mut outfile = outdir;
|
||||
outfile.push(fname);
|
||||
let man = Man::new(command);
|
||||
let mut buffer: Vec<u8> = vec![];
|
||||
man.render(&mut buffer)?;
|
||||
fs::write(&outfile, buffer)?;
|
||||
println!(" {}", outfile.display());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn manpages(prefix: &str) -> Result<(), io::Error> {
|
||||
println!("Generating Unix man pages:");
|
||||
COMMANDS
|
||||
.iter()
|
||||
.try_for_each(|cmd| crate::cmd::get(cmd).unwrap().manpage(prefix))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn completions(prefix: &str, matches: &ArgMatches) -> Result<(), io::Error> {
|
||||
println!("Generating completions:");
|
||||
if matches.get_flag("bash") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "bash-completion", "completion"]
|
||||
.iter()
|
||||
.collect();
|
||||
COMMANDS.iter().try_for_each(|cmd| {
|
||||
let cmd = crate::cmd::get(cmd).unwrap();
|
||||
cmd.completion(&outdir, "bash")
|
||||
})?;
|
||||
}
|
||||
if matches.get_flag("fish") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "fish", "completions"].iter().collect();
|
||||
COMMANDS.iter().try_for_each(|cmd| {
|
||||
let cmd = crate::cmd::get(cmd).unwrap();
|
||||
cmd.completion(&outdir, "fish")
|
||||
})?;
|
||||
}
|
||||
if matches.get_flag("nu") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "nu", "completions"].iter().collect();
|
||||
COMMANDS.iter().try_for_each(|cmd| {
|
||||
let cmd = crate::cmd::get(cmd).unwrap();
|
||||
cmd.completion(&outdir, "nu")
|
||||
})?;
|
||||
}
|
||||
if matches.get_flag("pwsh") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "pwsh", "completions"].iter().collect();
|
||||
COMMANDS.iter().try_for_each(|cmd| {
|
||||
let cmd = crate::cmd::get(cmd).unwrap();
|
||||
cmd.completion(&outdir, "pwsh")
|
||||
})?;
|
||||
}
|
||||
if matches.get_flag("zsh") || matches.get_flag("all") {
|
||||
let outdir: PathBuf = [prefix, "share", "zsh", "site-functions"].iter().collect();
|
||||
COMMANDS.iter().try_for_each(|cmd| {
|
||||
let cmd = crate::cmd::get(cmd).unwrap();
|
||||
cmd.completion(&outdir, "zsh")
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn links(prefix: &str, usr: bool, cmd: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||
println!("Generating links:");
|
||||
let mut binpath = PathBuf::from(prefix);
|
||||
binpath.push("bin");
|
||||
if !binpath.exists() {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
println!(" mkdir: {}", binpath.display());
|
||||
}
|
||||
binpath.pop();
|
||||
binpath.push("sbin");
|
||||
if !binpath.exists() {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
println!(" mkdir: {}", binpath.display());
|
||||
}
|
||||
if usr {
|
||||
binpath.pop();
|
||||
binpath.push("usr");
|
||||
binpath.push("bin");
|
||||
if !binpath.exists() {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
@ -277,25 +399,11 @@ impl<'a> Bootstrappable for Commands<'a> {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
println!(" mkdir: {}", binpath.display());
|
||||
}
|
||||
if usr {
|
||||
binpath.pop();
|
||||
binpath.push("usr");
|
||||
binpath.push("bin");
|
||||
if !binpath.exists() {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
println!(" mkdir: {}", binpath.display());
|
||||
}
|
||||
binpath.pop();
|
||||
binpath.push("sbin");
|
||||
if !binpath.exists() {
|
||||
fs::create_dir_all(&binpath)?;
|
||||
println!(" mkdir: {}", binpath.display());
|
||||
}
|
||||
}
|
||||
let soft = cmd.get_flag("soft");
|
||||
self.items
|
||||
.iter()
|
||||
.try_for_each(|cmd| cmd.link(prefix, usr, soft))?;
|
||||
Ok(())
|
||||
}
|
||||
let soft = cmd.get_flag("soft");
|
||||
COMMANDS.iter().try_for_each(|c| {
|
||||
let cmd = crate::cmd::get(c).unwrap();
|
||||
cmd.link(prefix, usr, soft)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,10 +8,14 @@ pub struct Dirname {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const DIRNAME: Dirname = Dirname {
|
||||
name: "dirname",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
};
|
||||
impl Default for Dirname {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "dirname",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Dirname {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -9,10 +9,14 @@ pub struct Echo {
|
||||
path: Option<Path>,
|
||||
}
|
||||
|
||||
pub const ECHO: Echo = Echo {
|
||||
name: "echo",
|
||||
path: Some(Path::Bin),
|
||||
};
|
||||
impl Default for Echo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "echo",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Echo {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -11,10 +11,14 @@ pub struct Factor {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const FACTOR: Factor = Factor {
|
||||
name: "factor",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
};
|
||||
impl Default for Factor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "factor",
|
||||
path: Some(crate::Path::UsrBin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Factor {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -9,10 +9,14 @@ pub struct False {
|
||||
path: Option<Path>,
|
||||
}
|
||||
|
||||
pub const FALSE: False = False {
|
||||
name: "false",
|
||||
path: Some(Path::Bin),
|
||||
};
|
||||
impl Default for False {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "false",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for False {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -15,10 +15,14 @@ pub struct Head {
|
||||
path: Option<Path>,
|
||||
}
|
||||
|
||||
pub const HEAD: Head = Head {
|
||||
name: "head",
|
||||
path: Some(Path::Bin),
|
||||
};
|
||||
impl Default for Head {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "head",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Head {
|
||||
fn name(&self) -> &str {
|
||||
|
157
src/cmd/mod.rs
157
src/cmd/mod.rs
@ -1,13 +1,5 @@
|
||||
use clap::ArgMatches;
|
||||
use clap_complete::{generate_to, Generator};
|
||||
use clap_mangen::Man;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt, fs, io,
|
||||
os::unix::fs::symlink,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
pub mod base32;
|
||||
pub mod base64;
|
||||
@ -38,123 +30,50 @@ mod sync;
|
||||
pub mod r#true;
|
||||
|
||||
pub use {
|
||||
self::base64::{Base64, BASE_64},
|
||||
self::hostname::{Hostname, HOSTNAME},
|
||||
base32::{Base32, BASE_32},
|
||||
bootstrap::{Bootstrap, BOOTSTRAP},
|
||||
dirname::{Dirname, DIRNAME},
|
||||
echo::{Echo, ECHO},
|
||||
factor::{Factor, FACTOR},
|
||||
head::{Head, HEAD},
|
||||
mountpoint::{Mountpoint, MOUNTPOINT},
|
||||
nologin::{Nologin, NOLOGIN},
|
||||
r#false::{False, FALSE},
|
||||
r#true::{True, TRUE},
|
||||
shitbox::{Shitbox, SHITBOX},
|
||||
sleep::{Sleep, SLEEP},
|
||||
self::hostname::Hostname, base32::Base32, base64::Base64, bootstrap::Bootstrap,
|
||||
dirname::Dirname, echo::Echo, factor::Factor, head::Head, mountpoint::Mountpoint,
|
||||
nologin::Nologin, r#false::False, r#true::True, shitbox::Shitbox, sleep::Sleep,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Commands<'a> {
|
||||
pub items: Vec<&'a dyn Cmd>,
|
||||
}
|
||||
|
||||
pub static COMMANDS: OnceCell<Commands> = OnceCell::new();
|
||||
|
||||
impl<'a> Commands<'a> {
|
||||
fn completion(outdir: &Path, cmd: &&dyn Cmd, gen: impl Generator) -> Result<(), io::Error> {
|
||||
let name = cmd.name();
|
||||
let mut cmd = cmd.cli();
|
||||
if !outdir.exists() {
|
||||
fs::create_dir_all(outdir)?;
|
||||
}
|
||||
let path = generate_to(gen, &mut cmd, name, outdir)?;
|
||||
println!(" {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Cmd: fmt::Debug + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn cli(&self) -> clap::Command;
|
||||
fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>>;
|
||||
fn path(&self) -> Option<crate::Path>;
|
||||
}
|
||||
|
||||
fn linkpath(&self, prefix: &str, usr: bool) -> Option<PathBuf> {
|
||||
let mut path = PathBuf::from(prefix);
|
||||
let binpath = self.path();
|
||||
match binpath {
|
||||
Some(crate::Path::Bin) => path.push("bin"),
|
||||
Some(crate::Path::Sbin) => path.push("sbin"),
|
||||
Some(crate::Path::UsrBin) => {
|
||||
if usr {
|
||||
path.push("usr");
|
||||
}
|
||||
path.push("bin");
|
||||
}
|
||||
Some(crate::Path::UsrSbin) => {
|
||||
if usr {
|
||||
path.push("usr");
|
||||
}
|
||||
path.push("sbin");
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
path.push(self.name());
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn link(&self, prefix: &str, usr: bool, soft: bool) -> Result<(), Box<dyn Error>> {
|
||||
if let Some(linkpath) = self.linkpath(prefix, usr) {
|
||||
if soft {
|
||||
let binpath = match self.path().unwrap() {
|
||||
crate::Path::Bin => "shitbox",
|
||||
crate::Path::Sbin => "../bin/shitbox",
|
||||
crate::Path::UsrBin => {
|
||||
if usr {
|
||||
"../../bin/shitbox"
|
||||
} else {
|
||||
"shitbox"
|
||||
}
|
||||
}
|
||||
crate::Path::UsrSbin => {
|
||||
if usr {
|
||||
"../../bin/shitbox"
|
||||
} else {
|
||||
"../bin/shitbox"
|
||||
}
|
||||
}
|
||||
};
|
||||
symlink(binpath, &linkpath)?;
|
||||
println!(" symlink: {binpath} -> {}", linkpath.display());
|
||||
} else {
|
||||
let mut binpath = PathBuf::from(prefix);
|
||||
binpath.push("bin");
|
||||
binpath.push(env!("CARGO_PKG_NAME"));
|
||||
fs::hard_link(&binpath, &linkpath)?;
|
||||
println!(" link: {} -> {}", binpath.display(), linkpath.display());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn manpage(&self, prefix: &str) -> Result<(), io::Error> {
|
||||
let command = self.cli();
|
||||
let fname = match self.name() {
|
||||
"bootstrap" => "shitbox-bootstrap.1".to_string(),
|
||||
s => format!("{s}.1"),
|
||||
};
|
||||
let outdir: PathBuf = [prefix, "usr", "share", "man", "man1"].iter().collect();
|
||||
if !outdir.exists() {
|
||||
fs::create_dir_all(&outdir)?;
|
||||
}
|
||||
let mut outfile = outdir;
|
||||
outfile.push(fname);
|
||||
let man = Man::new(command);
|
||||
let mut buffer: Vec<u8> = vec![];
|
||||
man.render(&mut buffer)?;
|
||||
fs::write(&outfile, buffer)?;
|
||||
println!(" {}", outfile.display());
|
||||
Ok(())
|
||||
pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
match name {
|
||||
"base64" => Some(Box::new(Base64::default())),
|
||||
"base32" => Some(Box::new(Base32::default())),
|
||||
"bootstrap" => Some(Box::new(Bootstrap::default())),
|
||||
"dirname" => Some(Box::new(Dirname::default())),
|
||||
"echo" => Some(Box::new(Echo::default())),
|
||||
"factor" => Some(Box::new(Factor::default())),
|
||||
"false" => Some(Box::new(False::default())),
|
||||
"head" => Some(Box::new(Head::default())),
|
||||
"mountpoint" => Some(Box::new(Mountpoint::default())),
|
||||
"nologin" => Some(Box::new(Nologin::default())),
|
||||
"shitbox" => Some(Box::new(Shitbox::default())),
|
||||
"sleep" => Some(Box::new(Sleep::default())),
|
||||
"true" => Some(Box::new(True::default())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub static COMMANDS: [&'static str; 14] = [
|
||||
"base32",
|
||||
"base64",
|
||||
"bootstrap",
|
||||
"dirname",
|
||||
"echo",
|
||||
"false",
|
||||
"factor",
|
||||
"head",
|
||||
"hostname",
|
||||
"mountpoint",
|
||||
"nologin",
|
||||
"true",
|
||||
"sleep",
|
||||
"shitbox",
|
||||
];
|
||||
|
@ -15,10 +15,14 @@ pub struct Mountpoint {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const MOUNTPOINT: Mountpoint = Mountpoint {
|
||||
name: "mountpoint",
|
||||
path: Some(crate::Path::Bin),
|
||||
};
|
||||
impl Default for Mountpoint {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "mountpoint",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Mountpoint {
|
||||
fn name(&self) -> &str {
|
||||
@ -38,7 +42,7 @@ impl Cmd for Mountpoint {
|
||||
failure: incorrect invocation, permissions or system error\
|
||||
\n 32\n \
|
||||
failure: the directory is not a mountpoint, or device is not a \
|
||||
block device on --devno"
|
||||
block device on --devno",
|
||||
)
|
||||
.args([
|
||||
Arg::new("fs-devno")
|
||||
|
@ -8,10 +8,14 @@ pub struct Nologin {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
pub const NOLOGIN: Nologin = Nologin {
|
||||
name: "nologin",
|
||||
path: Some(crate::Path::Sbin),
|
||||
};
|
||||
impl Default for Nologin {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "nologin",
|
||||
path: Some(crate::Path::Sbin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Nologin {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -1,11 +1,9 @@
|
||||
use super::{
|
||||
Cmd, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME, MOUNTPOINT,
|
||||
NOLOGIN, SLEEP, TRUE,
|
||||
};
|
||||
use super::{Cmd, COMMANDS};
|
||||
use clap::Command;
|
||||
use std::{
|
||||
error::Error,
|
||||
io::{self, ErrorKind},
|
||||
process,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -14,6 +12,15 @@ pub struct Shitbox {
|
||||
path: Option<crate::Path>,
|
||||
}
|
||||
|
||||
impl Default for Shitbox {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "shitbox",
|
||||
path: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const SHITBOX: Shitbox = Shitbox {
|
||||
name: "shitbox",
|
||||
path: None,
|
||||
@ -25,6 +32,18 @@ impl Cmd for Shitbox {
|
||||
}
|
||||
|
||||
fn cli(&self) -> clap::Command {
|
||||
let subcommands: Vec<Command> = {
|
||||
let mut s = vec![];
|
||||
for c in COMMANDS {
|
||||
if c == "shitbox" {
|
||||
continue;
|
||||
}
|
||||
if let Some(cmd) = crate::cmd::get(c) {
|
||||
s.push(cmd.cli());
|
||||
}
|
||||
}
|
||||
s
|
||||
};
|
||||
Command::new(self.name)
|
||||
.about("The Harbor Freight multitool of embedded Linux")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
@ -32,42 +51,20 @@ impl Cmd for Shitbox {
|
||||
.arg_required_else_help(true)
|
||||
.subcommand_value_name("APPLET")
|
||||
.subcommand_help_heading("APPLETS")
|
||||
.subcommands([
|
||||
BASE_32.cli(),
|
||||
BASE_64.cli(),
|
||||
BOOTSTRAP.cli(),
|
||||
DIRNAME.cli(),
|
||||
ECHO.cli(),
|
||||
FALSE.cli(),
|
||||
FACTOR.cli(),
|
||||
HEAD.cli(),
|
||||
MOUNTPOINT.cli(),
|
||||
NOLOGIN.cli(),
|
||||
HOSTNAME.cli(),
|
||||
SLEEP.cli(),
|
||||
TRUE.cli(),
|
||||
])
|
||||
.subcommands(&subcommands)
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn Error>> {
|
||||
let Some(matches) = matches else {
|
||||
return Err(Box::new(io::Error::new(ErrorKind::Other, "No input")));
|
||||
};
|
||||
match matches.subcommand() {
|
||||
Some(("base32", matches)) => BASE_32.run(Some(matches))?,
|
||||
Some(("base64", matches)) => BASE_64.run(Some(matches))?,
|
||||
Some(("bootstrap", matches)) => BOOTSTRAP.run(Some(matches))?,
|
||||
Some(("dirname", matches)) => DIRNAME.run(Some(matches))?,
|
||||
Some(("echo", _matches)) => ECHO.run(None)?,
|
||||
Some(("factor", matches)) => FACTOR.run(Some(matches))?,
|
||||
Some(("false", _matches)) => FALSE.run(None)?,
|
||||
Some(("head", matches)) => HEAD.run(Some(matches))?,
|
||||
Some(("mountpoint", matches)) => MOUNTPOINT.run(Some(matches))?,
|
||||
Some(("nologin", _matches)) => NOLOGIN.run(None)?,
|
||||
Some(("hostname", matches)) => HOSTNAME.run(Some(matches))?,
|
||||
Some(("sleep", matches)) => SLEEP.run(Some(matches))?,
|
||||
Some(("true", _matches)) => TRUE.run(None)?,
|
||||
_ => unimplemented!(),
|
||||
if let Some((name, matches)) = matches.subcommand() {
|
||||
if let Some(command) = crate::cmd::get(name) {
|
||||
if let Err(e) = command.run(Some(matches)) {
|
||||
eprintln!("Error: {name}: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,10 +9,14 @@ pub struct Sleep {
|
||||
path: Option<Path>,
|
||||
}
|
||||
|
||||
pub const SLEEP: Sleep = Sleep {
|
||||
name: "sleep",
|
||||
path: Some(Path::Bin),
|
||||
};
|
||||
impl Default for Sleep {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "sleep",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for Sleep {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -9,10 +9,14 @@ pub struct True {
|
||||
path: Option<Path>,
|
||||
}
|
||||
|
||||
pub const TRUE: True = True {
|
||||
name: "true",
|
||||
path: Some(Path::Bin),
|
||||
};
|
||||
impl Default for True {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "true",
|
||||
path: Some(crate::Path::Bin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cmd for True {
|
||||
fn name(&self) -> &str {
|
||||
|
57
src/lib.rs
57
src/lib.rs
@ -1,11 +1,8 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use std::{env, error::Error, path::PathBuf, string::ToString};
|
||||
use std::{env, error::Error, path::PathBuf, process, string::ToString};
|
||||
|
||||
pub mod cmd;
|
||||
pub use cmd::{
|
||||
Cmd, Commands, BASE_32, BASE_64, BOOTSTRAP, DIRNAME, ECHO, FACTOR, FALSE, HEAD, HOSTNAME,
|
||||
MOUNTPOINT, NOLOGIN, SHITBOX, SLEEP, TRUE,
|
||||
};
|
||||
pub use cmd::Cmd;
|
||||
pub mod math;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -37,50 +34,16 @@ pub fn progpath() -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), Box<dyn Error>> {
|
||||
if cmd::COMMANDS.get().is_none() {
|
||||
cmd::COMMANDS
|
||||
.set(Commands {
|
||||
items: vec![
|
||||
&BASE_32,
|
||||
&BASE_64,
|
||||
&BOOTSTRAP,
|
||||
&DIRNAME,
|
||||
&ECHO,
|
||||
&FALSE,
|
||||
&FACTOR,
|
||||
&HEAD,
|
||||
&HOSTNAME,
|
||||
&MOUNTPOINT,
|
||||
&NOLOGIN,
|
||||
&TRUE,
|
||||
&SLEEP,
|
||||
&SHITBOX,
|
||||
],
|
||||
})
|
||||
.expect("Cannot register commands");
|
||||
}
|
||||
if let Some(progname) = progname() {
|
||||
match progname.as_str() {
|
||||
"base32" => BASE_32.run(Some(&BASE_32.cli().get_matches()))?,
|
||||
"base64" => BASE_64.run(Some(&BASE_64.cli().get_matches()))?,
|
||||
"dirname" => DIRNAME.run(Some(&DIRNAME.cli().get_matches()))?,
|
||||
"echo" => ECHO.run(None)?,
|
||||
"false" => FALSE.run(None)?,
|
||||
"factor" => FACTOR.run(Some(&FACTOR.cli().get_matches()))?,
|
||||
"head" => {
|
||||
HEAD.run(Some(&HEAD.cli().get_matches()))?;
|
||||
if let Some(command) = cmd::get(&progname) {
|
||||
let cli = command.cli();
|
||||
if let Err(e) = command.run(Some(&cli.get_matches())) {
|
||||
eprintln!("{progname}: Error: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
"hostname" => HOSTNAME.run(Some(&HOSTNAME.cli().get_matches()))?,
|
||||
"mountpoint" => MOUNTPOINT.run(Some(&MOUNTPOINT.cli().get_matches()))?,
|
||||
"nologin" => NOLOGIN.run(None)?,
|
||||
"true" => TRUE.run(None)?,
|
||||
"shitbox" => {
|
||||
SHITBOX.run(Some(&SHITBOX.cli().get_matches()))?;
|
||||
}
|
||||
"sleep" => {
|
||||
SLEEP.run(Some(&SLEEP.cli().get_matches()))?;
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
} else {
|
||||
eprintln!("shitbox: Error: unknown command {progname}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
Loading…
Reference in New Issue
Block a user