1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
//! <!-- Override references in README.md back to `cargo doc`-generated links. -->
//!
//! [`&str`]: `&str`
//! [`String`]: `String`
//! [`bstr::BStr`]: `bstr::BStr`
//! [`bstr::BString`]: `bstr::BString`
//! [`slice`]: `slice`
//! [`Vec<u8>`]: `std::vec::Vec<u8>`
//! [`OsStr`]: `std::ffi::OsStr`
//! [`OsString`]: `std::ffi::OsString`
//! [`Path`]: `std::path::Path`
//! [`PathBuf`]: `std::path::PathBuf`
//!
//! [`Sh`]: `Sh`
//! [`Dash`]: `Dash`
//! [`Bash`]: `Bash`
//! [`Fish`]: `Fish`
//! [`Zsh`]: `Zsh`
//!
//! [`QuoteRefExt`]: `QuoteRefExt`
//! [`QuoteRefExt::quoted`]: `QuoteRefExt::quoted`
//! [`QuoteExt`]: `QuoteExt`
//!
//! <style>
//! .readme-only { display: none; }
//! </style>
//!
#![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};
mod ascii;
mod bash;
mod fish;
mod sh;
mod utf8;
#[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`].
#[cfg(feature = "sh")]
pub type Dash = sh::Sh;
/// Zsh accepts the same quoted/escaped strings as Bash, hence this is an alias
/// for [`Bash`].
#[cfg(feature = "bash")]
pub type Zsh = bash::Bash;
// ----------------------------------------------------------------------------
/// Quoting/escaping a string of bytes into a shell-safe form.
pub trait QuoteInto<OUT: ?Sized> {
/// Quote/escape a string of bytes into an existing container.
fn quote_into<'q, S: ?Sized + Into<Quotable<'q>>>(s: S, out: &mut OUT);
}
/// Quoting/escaping a string of bytes into a shell-safe form.
pub trait Quote<OUT: Default>: QuoteInto<OUT> {
/// Quote/escape a string of bytes into a new container.
fn quote<'q, S: ?Sized + Into<Quotable<'q>>>(s: S) -> OUT {
let mut out = OUT::default();
Self::quote_into(s, &mut out);
out
}
}
/// Blanket [`Quote`] impl for anything that has a [`QuoteInto`] impl.
impl<T: QuoteInto<OUT>, OUT: Default> Quote<OUT> for T {}
// ----------------------------------------------------------------------------
/// Extension trait for pushing shell quoted byte slices, e.g. `&[u8]`, [`&str`]
/// – anything that's [`Quotable`] – into container types like [`Vec<u8>`],
/// [`String`], [`OsString`] on Unix, and [`bstr::BString`] if it's enabled.
pub trait QuoteExt {
fn push_quoted<'q, Q, S>(&mut self, _q: Q, s: S)
where
Q: QuoteInto<Self>,
S: ?Sized + Into<Quotable<'q>>;
}
impl<T: ?Sized> QuoteExt for T {
fn push_quoted<'q, Q, S>(&mut self, _q: Q, s: S)
where
Q: QuoteInto<Self>,
S: ?Sized + Into<Quotable<'q>>,
{
Q::quote_into(s, self);
}
}
// ----------------------------------------------------------------------------
/// Extension trait for shell quoting many different owned and reference types,
/// e.g. `&[u8]`, [`&str`] – anything that's [`Quotable`] – into owned container
/// types like [`Vec<u8>`], [`String`], [`OsString`] on Unix, and
/// [`bstr::BString`] if it's enabled.
pub trait QuoteRefExt<Output: Default> {
fn quoted<Q: Quote<Output>>(self, q: Q) -> Output;
}
impl<'a, S, OUT: Default> QuoteRefExt<OUT> for S
where
S: ?Sized + Into<Quotable<'a>>,
{
fn quoted<Q: Quote<OUT>>(self, _q: Q) -> OUT {
Q::quote(self)
}
}
// ----------------------------------------------------------------------------
/// A string of bytes that can be quoted/escaped.
///
/// This is used by many methods in this crate as a generic
/// [`Into<Quotable>`][`Into`] constraint. Why not accept
/// [`AsRef<[u8]>`][`AsRef`] instead? The ergonomics of that approach were not
/// so good. For example, quoting [`OsString`]/[`OsStr`] and
/// [`PathBuf`]/[`Path`] didn't work in a natural way.
pub enum Quotable<'a> {
#[cfg_attr(
not(any(feature = "bash", feature = "fish", feature = "sh")),
allow(unused)
)]
Bytes(&'a [u8]),
#[cfg_attr(
not(any(feature = "bash", feature = "fish", feature = "sh")),
allow(unused)
)]
Text(&'a str),
}
impl<'a> From<&'a [u8]> for Quotable<'a> {
fn from(source: &'a [u8]) -> Quotable<'a> {
Quotable::Bytes(source)
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for Quotable<'a> {
fn from(source: &'a [u8; N]) -> Quotable<'a> {
Quotable::Bytes(&source[..])
}
}
impl<'a> From<&'a Vec<u8>> for Quotable<'a> {
fn from(source: &'a Vec<u8>) -> Quotable<'a> {
Quotable::Bytes(source)
}
}
impl<'a> From<&'a str> for Quotable<'a> {
fn from(source: &'a str) -> Quotable<'a> {
Quotable::Text(source)
}
}
impl<'a> From<&'a String> for Quotable<'a> {
fn from(source: &'a String) -> Quotable<'a> {
Quotable::Text(source)
}
}
#[cfg(unix)]
impl<'a> From<&'a OsStr> for Quotable<'a> {
fn from(source: &'a OsStr) -> Quotable<'a> {
use std::os::unix::ffi::OsStrExt;
source.as_bytes().into()
}
}
#[cfg(unix)]
impl<'a> From<&'a OsString> for Quotable<'a> {
fn from(source: &'a OsString) -> Quotable<'a> {
use std::os::unix::ffi::OsStrExt;
source.as_bytes().into()
}
}
#[cfg(feature = "bstr")]
impl<'a> From<&'a bstr::BStr> for Quotable<'a> {
fn from(source: &'a bstr::BStr) -> Quotable<'a> {
let bytes: &[u8] = source.as_ref();
bytes.into()
}
}
#[cfg(feature = "bstr")]
impl<'a> From<&'a bstr::BString> for Quotable<'a> {
fn from(source: &'a bstr::BString) -> Quotable<'a> {
let bytes: &[u8] = source.as_ref();
bytes.into()
}
}
#[cfg(unix)]
impl<'a> From<&'a Path> for Quotable<'a> {
fn from(source: &'a Path) -> Quotable<'a> {
source.as_os_str().into()
}
}
#[cfg(unix)]
impl<'a> From<&'a PathBuf> for Quotable<'a> {
fn from(source: &'a PathBuf) -> Quotable<'a> {
source.as_os_str().into()
}
}