Skip to content

Commit

Permalink
Add feature flags for each shell
Browse files Browse the repository at this point in the history
  • Loading branch information
allenap committed Jul 3, 2024
1 parent a3f5aac commit d2f2009
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 25 deletions.
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"]
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 5 additions & 3 deletions src/ascii.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -71,6 +73,7 @@ impl Char {
}

#[inline]
#[cfg(feature = "sh")]
pub fn code(&self) -> u8 {
use Char::*;
match *self {
Expand Down Expand Up @@ -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());
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/bash.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/fish.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(feature = "fish")]

use crate::{
ascii::Char, quoter::QuoterSealed, util::u8_to_hex_escape_uppercase_x, Quotable, Quoter,
};
Expand Down
23 changes: 20 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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<u8>`],
Expand Down Expand Up @@ -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],
}

Expand Down
2 changes: 2 additions & 0 deletions src/sh.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(feature = "sh")]

use crate::{ascii::Char, quoter::QuoterSealed, Quotable, Quoter};

/// Quote byte strings for use with `/bin/sh`.
Expand Down
30 changes: 12 additions & 18 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -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";
[
Expand All @@ -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";
[
Expand All @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/test_bash.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(feature = "bash")]

mod util;

// -- impl Bash ---------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions tests/test_fish.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(feature = "fish")]

mod util;

// -- impl Fish ---------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions tests/test_sh.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(feature = "sh")]

mod util;

// -- Helpers -----------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions tests/test_ux.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(all(feature = "bash", feature = "bstr"))]

use bstr::{BString, ByteSlice};
use std::{ffi::OsString, os::unix::ffi::OsStringExt};

Expand Down

0 comments on commit d2f2009

Please sign in to comment.