diff --git a/src/bash.rs b/src/bash.rs index 42e329b..ce51041 100644 --- a/src/bash.rs +++ b/src/bash.rs @@ -124,6 +124,7 @@ impl Bash { /// pub fn quote_vec<'a, S: ?Sized + Into>>(s: S) -> Vec { match s.into() { + Quotable::Byte(byte) => Self::quote_vec(&[byte]), Quotable::Bytes(bytes) => match bytes::escape_prepare(bytes) { bytes::Prepared::Empty => vec![b'\'', b'\''], bytes::Prepared::Inert => bytes.into(), @@ -137,6 +138,7 @@ impl Bash { sout } }, + Quotable::Char(ch) => Self::quote_vec(&ch.to_string()), Quotable::Text(text) => match text::escape_prepare(text) { text::Prepared::Empty => vec![b'\'', b'\''], text::Prepared::Inert => text.into(), @@ -170,6 +172,7 @@ impl Bash { /// pub fn quote_into_vec<'a, S: ?Sized + Into>>(s: S, sout: &mut Vec) { match s.into() { + Quotable::Byte(byte) => Self::quote_into_vec(&[byte], sout), Quotable::Bytes(bytes) => match bytes::escape_prepare(bytes) { bytes::Prepared::Empty => sout.extend(b"''"), bytes::Prepared::Inert => sout.extend(bytes), @@ -184,6 +187,7 @@ impl Bash { debug_assert_eq!(cap, sout.capacity()); // No reallocations. } }, + Quotable::Char(ch) => Self::quote_into_vec(&ch.to_string(), sout), Quotable::Text(text) => match text::escape_prepare(text) { text::Prepared::Empty => sout.extend(b"''"), text::Prepared::Inert => sout.extend(text.as_bytes()), diff --git a/src/fish.rs b/src/fish.rs index 673d315..1e1a2a8 100644 --- a/src/fish.rs +++ b/src/fish.rs @@ -90,6 +90,7 @@ impl Fish { /// ``` pub fn quote_vec<'a, S: ?Sized + Into>>(s: S) -> Vec { match s.into() { + Quotable::Byte(byte) => Self::quote_vec(&[byte]), Quotable::Bytes(bytes) => match bytes::escape_prepare(bytes) { bytes::Prepared::Empty => vec![b'\'', b'\''], bytes::Prepared::Inert => bytes.into(), @@ -99,6 +100,7 @@ impl Fish { sout } }, + Quotable::Char(ch) => Self::quote_vec(&ch.to_string()), Quotable::Text(text) => match text::escape_prepare(text) { text::Prepared::Empty => vec![b'\'', b'\''], text::Prepared::Inert => text.into(), @@ -128,6 +130,7 @@ impl Fish { /// pub fn quote_into_vec<'a, S: ?Sized + Into>>(s: S, sout: &mut Vec) { match s.into() { + Quotable::Byte(byte) => Self::quote_into_vec(&[byte], sout), Quotable::Bytes(bytes) => match bytes::escape_prepare(bytes) { bytes::Prepared::Empty => sout.extend(b"''"), bytes::Prepared::Inert => sout.extend(bytes), @@ -136,6 +139,7 @@ impl Fish { bytes::escape_chars(esc, sout); // Do the work. } }, + Quotable::Char(ch) => Self::quote_into_vec(&ch.to_string(), sout), Quotable::Text(text) => match text::escape_prepare(text) { text::Prepared::Empty => sout.extend(b"''"), text::Prepared::Inert => sout.extend(text.as_bytes()), diff --git a/src/lib.rs b/src/lib.rs index fe8f86f..685d614 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,11 @@ where /// 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) + )] + Byte(u8), #[cfg_attr( not(any(feature = "bash", feature = "fish", feature = "sh")), allow(unused) @@ -116,9 +121,26 @@ pub enum Quotable<'a> { not(any(feature = "bash", feature = "fish", feature = "sh")), allow(unused) )] + Char(char), + #[cfg_attr( + not(any(feature = "bash", feature = "fish", feature = "sh")), + allow(unused) + )] Text(&'a str), } +impl From for Quotable<'static> { + fn from(source: u8) -> Quotable<'static> { + Quotable::Byte(source) + } +} + +impl From for Quotable<'static> { + fn from(source: char) -> Quotable<'static> { + Quotable::Char(source) + } +} + impl<'a> From<&'a [u8]> for Quotable<'a> { fn from(source: &'a [u8]) -> Quotable<'a> { Quotable::Bytes(source) diff --git a/src/sh.rs b/src/sh.rs index ef032f0..1732e3e 100644 --- a/src/sh.rs +++ b/src/sh.rs @@ -136,13 +136,21 @@ impl Sh { /// ``` /// pub fn quote_vec<'a, S: ?Sized + Into>>(s: S) -> Vec { - let bytes = match s.into() { - Quotable::Bytes(bytes) => bytes, - Quotable::Text(s) => s.as_bytes(), + let quotable = s.into(); + let prepared = match quotable { + Quotable::Byte(byte) => escape_prepare(&[byte]), + Quotable::Bytes(bytes) => escape_prepare(bytes), + Quotable::Char(ch) => escape_prepare(ch.to_string().as_bytes()), + Quotable::Text(s) => escape_prepare(s.as_bytes()), }; - match escape_prepare(bytes) { + match prepared { Prepared::Empty => vec![b'\'', b'\''], - Prepared::Inert => bytes.into(), + Prepared::Inert => match quotable { + Quotable::Byte(byte) => vec![byte], + Quotable::Bytes(bytes) => bytes.to_owned(), + Quotable::Char(ch) => ch.to_string().into(), + Quotable::Text(s) => s.as_bytes().into(), + }, Prepared::Escape(esc) => { // This may be a pointless optimisation, but calculate the // memory needed to avoid reallocations as we construct the @@ -180,13 +188,21 @@ impl Sh { /// ``` /// pub fn quote_into_vec<'a, S: ?Sized + Into>>(s: S, sout: &mut Vec) { - let bytes = match s.into() { - Quotable::Bytes(bytes) => bytes, - Quotable::Text(s) => s.as_bytes(), + let quotable = s.into(); + let prepared = match quotable { + Quotable::Byte(byte) => escape_prepare(&[byte]), + Quotable::Bytes(bytes) => escape_prepare(bytes), + Quotable::Char(ch) => escape_prepare(ch.to_string().as_bytes()), + Quotable::Text(s) => escape_prepare(s.as_bytes()), }; - match escape_prepare(bytes) { + match prepared { Prepared::Empty => sout.extend(b"''"), - Prepared::Inert => sout.extend(bytes), + Prepared::Inert => match quotable { + Quotable::Byte(byte) => sout.push(byte), + Quotable::Bytes(bytes) => sout.extend(bytes), + Quotable::Char(ch) => sout.extend(ch.to_string().as_bytes()), + Quotable::Text(s) => sout.extend(s.as_bytes()), + }, Prepared::Escape(esc) => { // This may be a pointless optimisation, but calculate the // memory needed to avoid reallocations as we construct the