From d2f2009f72dfb266f680928ad6d18af68226c241 Mon Sep 17 00:00:00 2001 From: Gavin Panella Date: Wed, 3 Jul 2024 17:32:10 +0200 Subject: [PATCH] Add feature flags for each shell --- Cargo.toml | 8 +++++++- README.md | 17 +++++++++++++++++ src/ascii.rs | 8 +++++--- src/bash.rs | 2 ++ src/fish.rs | 2 ++ src/lib.rs | 23 ++++++++++++++++++++--- src/sh.rs | 2 ++ src/util.rs | 30 ++++++++++++------------------ tests/test_bash.rs | 2 ++ tests/test_fish.rs | 2 ++ tests/test_sh.rs | 2 ++ tests/test_ux.rs | 2 ++ 12 files changed, 75 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0873b5..65a871d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,10 @@ repository = "https://github.com/allenap/shell-quote" version = "0.6.1" [features] -default = ["bstr"] +default = ["bstr", "bash", "sh", "fish"] +bash = [] +fish = [] +sh = [] [dependencies] bstr = { version = "1", optional = true } @@ -23,11 +26,14 @@ criterion = { version = "^0.5.1", features = ["html_reports"] } [[bench]] name = "bash" harness = false +required-features = ["bash"] [[bench]] name = "sh" harness = false +required-features = ["sh"] [[bench]] name = "fish" harness = false +required-features = ["fish"] diff --git a/README.md b/README.md index 196d950..85aa834 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,23 @@ Inspired by the Haskell [shell-escape][] package. `/bin/sh`-like shells like Dash. However, fish's quoting rules are different enough that you must use [`Fish`] for fish scripts. +## Feature flags + +The following are all enabled by default: + +- `bstr`: Support [`bstr::BStr`] and [`bstr::BString`]. +- `bash`: Support [Bash][gnu-bash] and [Z Shell][z-shell]. +- `fish`: Support [fish][]. +- `sh`: Support `/bin/sh`-like shells including [Dash][dash]. + +To limit support to specific shells, you must disable this crate's default +features in `Cargo.toml` and re-enable those you want. For example: + +```toml +[dependencies] +shell-quote = { version = "*", default-features = false, features = ["bash"] } +``` + ## Examples When quoting using raw bytes it can be convenient to call [`Sh`]'s, [`Dash`]'s, diff --git a/src/ascii.rs b/src/ascii.rs index 3f89d09..d81cf7e 100644 --- a/src/ascii.rs +++ b/src/ascii.rs @@ -1,3 +1,5 @@ +#![cfg(any(feature = "bash", feature = "fish", feature = "sh"))] + //! Scanner for ASCII control codes, shell metacharacters, printable characters, //! and extended codes, i.e. classify each byte in a stream according to where //! it appears in extended ASCII. @@ -71,6 +73,7 @@ impl Char { } #[inline] + #[cfg(feature = "sh")] pub fn code(&self) -> u8 { use Char::*; match *self { @@ -106,12 +109,11 @@ const DEL: u8 = 0x7F; #[cfg(test)] mod tests { - use super::Char; - #[test] + #[cfg(feature = "sh")] fn test_code() { for ch in u8::MIN..=u8::MAX { - let char = Char::from(ch); + let char = super::Char::from(ch); assert_eq!(ch, char.code()); } } diff --git a/src/bash.rs b/src/bash.rs index 5f6431b..bfa7bbb 100644 --- a/src/bash.rs +++ b/src/bash.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "bash")] + use crate::{ascii::Char, quoter::QuoterSealed, util::u8_to_hex_escape, Quotable, Quoter}; /// Quote byte strings for use with Bash, the GNU Bourne-Again Shell. diff --git a/src/fish.rs b/src/fish.rs index b6f7b72..20998fd 100644 --- a/src/fish.rs +++ b/src/fish.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "fish")] + use crate::{ ascii::Char, quoter::QuoterSealed, util::u8_to_hex_escape_uppercase_x, Quotable, Quoter, }; diff --git a/src/lib.rs b/src/lib.rs index 964f30c..c135ea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,12 @@ -#![doc = include_str!("../README.md")] +#![cfg_attr( + all( + feature = "bstr", + feature = "bash", + feature = "fish", + feature = "sh", + ), + doc = include_str!("../README.md") +)] use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; @@ -9,17 +17,22 @@ mod fish; mod sh; pub(crate) mod util; +#[cfg(feature = "bash")] pub use bash::Bash; +#[cfg(feature = "fish")] pub use fish::Fish; +#[cfg(feature = "sh")] pub use sh::Sh; /// Dash accepts the same quoted/escaped strings as `/bin/sh` – indeed, on many /// systems, `dash` _is_ `/bin/sh` – hence this is an alias for [`Sh`]. -pub type Dash = Sh; +#[cfg(feature = "sh")] +pub type Dash = sh::Sh; /// Zsh accepts the same quoted/escaped strings as Bash, hence this is an alias /// for [`Bash`]. -pub type Zsh = Bash; +#[cfg(feature = "bash")] +pub type Zsh = bash::Bash; /// Extension trait for pushing shell quoted byte slices, e.g. `&[u8]`, [`&str`] /// – anything that's [`Quotable`] – into byte container types like [`Vec`], @@ -146,6 +159,10 @@ pub trait Quoter: quoter::QuoterSealed {} /// so good. For example, quoting [`OsString`]/[`OsStr`] and /// [`PathBuf`]/[`Path`] didn't work in a natural way. pub struct Quotable<'a> { + #[cfg_attr( + not(any(feature = "bash", feature = "fish", feature = "sh")), + allow(unused) + )] pub(crate) bytes: &'a [u8], } diff --git a/src/sh.rs b/src/sh.rs index 10b81d8..cc3d0f6 100644 --- a/src/sh.rs +++ b/src/sh.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "sh")] + use crate::{ascii::Char, quoter::QuoterSealed, Quotable, Quoter}; /// Quote byte strings for use with `/bin/sh`. diff --git a/src/util.rs b/src/util.rs index c969fca..30ae2bc 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,17 +1,9 @@ -/// Represent a single byte as a 2-byte hex number. -#[allow(unused)] -pub(crate) fn u8_to_hex(ch: u8) -> [u8; 2] { - const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; - [ - HEX_DIGITS[(ch >> 4) as usize], - HEX_DIGITS[(ch & 0xF) as usize], - ] -} - /// Escape a byte as a 4-byte hex escape sequence. /// /// The `\\xHH` format (backslash, a literal "x", two hex characters) is /// understood by many shells. +#[inline] +#[cfg(feature = "bash")] pub(crate) fn u8_to_hex_escape(ch: u8) -> [u8; 4] { const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; [ @@ -34,6 +26,8 @@ pub(crate) fn u8_to_hex_escape(ch: u8) -> [u8; 4] { /// /// [release notes]: https://github.com/fish-shell/fish-shell/releases/tag/3.6.0 /// +#[inline] +#[cfg(feature = "fish")] pub(crate) fn u8_to_hex_escape_uppercase_x(ch: u8) -> [u8; 4] { const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; [ @@ -46,23 +40,23 @@ pub(crate) fn u8_to_hex_escape_uppercase_x(ch: u8) -> [u8; 4] { #[cfg(test)] mod tests { - use super::*; - #[test] - fn test_u8_to_hex() { + #[cfg(feature = "bash")] + fn test_u8_to_hex_escape() { for ch in u8::MIN..=u8::MAX { - let expected = format!("{ch:02X}"); - let observed = u8_to_hex(ch); + let expected = format!("\\x{ch:02X}"); + let observed = super::u8_to_hex_escape(ch); let observed = std::str::from_utf8(&observed).unwrap(); assert_eq!(observed, &expected); } } #[test] - fn test_u8_to_hex_escape() { + #[cfg(feature = "fish")] + fn test_u8_to_hex_escape_uppercase_x() { for ch in u8::MIN..=u8::MAX { - let expected = format!("\\x{ch:02X}"); - let observed = u8_to_hex_escape(ch); + let expected = format!("\\X{ch:02X}"); + let observed = super::u8_to_hex_escape_uppercase_x(ch); let observed = std::str::from_utf8(&observed).unwrap(); assert_eq!(observed, &expected); } diff --git a/tests/test_bash.rs b/tests/test_bash.rs index a822ed2..0eff885 100644 --- a/tests/test_bash.rs +++ b/tests/test_bash.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "bash")] + mod util; // -- impl Bash --------------------------------------------------------------- diff --git a/tests/test_fish.rs b/tests/test_fish.rs index 48c097d..e77052f 100644 --- a/tests/test_fish.rs +++ b/tests/test_fish.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "fish")] + mod util; // -- impl Fish --------------------------------------------------------------- diff --git a/tests/test_sh.rs b/tests/test_sh.rs index 4f1b675..e0ac865 100644 --- a/tests/test_sh.rs +++ b/tests/test_sh.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "sh")] + mod util; // -- Helpers ----------------------------------------------------------------- diff --git a/tests/test_ux.rs b/tests/test_ux.rs index 2eebe87..0d74339 100644 --- a/tests/test_ux.rs +++ b/tests/test_ux.rs @@ -1,3 +1,5 @@ +#![cfg(all(feature = "bash", feature = "bstr"))] + use bstr::{BString, ByteSlice}; use std::{ffi::OsString, os::unix::ffi::OsStringExt};