Skip to content

Commit deed1f1

Browse files
committed
refactor
- adapt to crate layout of git-quote - add more tests
1 parent acb4c17 commit deed1f1

File tree

5 files changed

+69
-52
lines changed

5 files changed

+69
-52
lines changed

git-quote/src/ansi_c.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
///
12
pub mod undo {
23
use bstr::{BStr, BString};
34
use quick_error::quick_error;
45

56
quick_error! {
7+
/// The error returned by [ansi_c][crate::ansi_c::undo()].
68
#[derive(Debug)]
9+
#[allow(missing_docs)]
710
pub enum Error {
811
InvalidInput { message: String, input: BString } {
912
display("{}: {:?}", message, input)

git-quote/src/lib.rs

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,9 @@
1-
#![deny(rust_2018_idioms)]
1+
//! Provides functions to quote and possibly unquote strings with different quoting styles.
2+
#![deny(rust_2018_idioms, missing_docs)]
23
#![forbid(unsafe_code)]
34

4-
use bstr::{BStr, BString, ByteSlice};
5-
65
///
76
pub mod ansi_c;
87

9-
/// Transforms the given value to be suitable for use as an argument for Bourne shells by wrapping in single quotes
10-
pub fn to_single_quoted(mut value: &BStr) -> BString {
11-
let mut quoted = BString::new(b"'".to_vec());
12-
13-
while let Some(pos) = value.find_byteset(b"!'") {
14-
quoted.extend_from_slice(&value[..pos]);
15-
quoted.extend_from_slice(b"'\\");
16-
quoted.push(value[pos]);
17-
quoted.push(b'\'');
18-
19-
value = &value[pos + 1..];
20-
}
21-
22-
quoted.extend_from_slice(value);
23-
quoted.push(b'\'');
24-
quoted
25-
}
26-
27-
#[cfg(test)]
28-
mod tests {
29-
use crate::to_single_quoted;
30-
use bstr::BStr;
31-
32-
#[test]
33-
fn quoted_strings() {
34-
assert_eq!(to_single_quoted("my cool string".into()), "'my cool string'");
35-
assert_eq!(to_single_quoted(r"'\''".into()), BStr::new(r"''\''\'\'''\'''"));
36-
assert_eq!(
37-
to_single_quoted("my 'quoted' string".into()),
38-
BStr::new(r"'my '\''quoted'\'' string'")
39-
);
40-
assert_eq!(to_single_quoted(r"'\!'".into()), BStr::new(r"''\''\'\!''\'''"));
41-
assert_eq!(
42-
to_single_quoted("my excited string!!!".into()),
43-
BStr::new(r"'my excited string'\!''\!''\!''")
44-
);
45-
assert_eq!(
46-
to_single_quoted("\0my `even` ~cooler~ $t\\'ring\\// with \"quotes!\"".into()),
47-
BStr::new("'\0my `even` ~cooler~ $t\\'\\''ring\\// with \"quotes'\\!'\"'")
48-
);
49-
}
50-
}
8+
mod single;
9+
pub use single::single;

git-quote/src/single.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use bstr::{BStr, BString, ByteSlice};
2+
3+
/// Transforms the given `value` to be suitable for use as an argument for Bourne shells by wrapping it into single quotes.
4+
///
5+
/// Every single-quote `'` is escaped with `\'`, every exclamation mark `!` is escaped with `\!`, and the entire string is enclosed
6+
/// in single quotes.
7+
pub fn single(mut value: &BStr) -> BString {
8+
let mut quoted = BString::new(b"'".to_vec());
9+
10+
while let Some(pos) = value.find_byteset(b"!'") {
11+
quoted.extend_from_slice(&value[..pos]);
12+
quoted.push(b'\\');
13+
quoted.push(value[pos]);
14+
15+
value = &value[pos + 1..];
16+
}
17+
18+
quoted.extend_from_slice(value);
19+
quoted.push(b'\'');
20+
quoted
21+
}

git-quote/tests/quote.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,39 @@
1+
mod single {
2+
use git_quote::single;
3+
4+
#[test]
5+
fn empty() {
6+
assert_eq!(single("".into()), "''");
7+
}
8+
9+
#[test]
10+
fn unquoted_becomes_quoted() {
11+
assert_eq!(single("a".into()), "'a'");
12+
assert_eq!(single("a b".into()), "'a b'");
13+
assert_eq!(single("a\nb".into()), "'a\nb'", "newlines play no role");
14+
}
15+
16+
#[test]
17+
fn existing_exclamation_mark_gets_escaped() {
18+
assert_eq!(single(r"a!b".into()), r"'a\!b'");
19+
assert_eq!(single(r"!".into()), r"'\!'");
20+
assert_eq!(single(r"\!".into()), r"'\\!'");
21+
}
22+
23+
#[test]
24+
fn existing_quote_gets_escaped() {
25+
assert_eq!(single(r"a'b".into()), r"'a\'b'");
26+
assert_eq!(single(r"'".into()), r"'\''");
27+
assert_eq!(single(r"'\''".into()), r"'\'\\'\''");
28+
}
29+
30+
#[test]
31+
fn complex() {
32+
let expected = "'\0cmd `arg` $var\\\\'ring\\// arg \"quoted\\!\"'";
33+
assert_eq!(single("\0cmd `arg` $var\\'ring\\// arg \"quoted!\"".into()), expected);
34+
}
35+
}
36+
137
mod ansi_c {
238
mod undo {
339
use bstr::ByteSlice;

git-transport/src/client/blocking_io/file.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ use std::{
1010

1111
use bstr::{io::BufReadExt, BStr, BString, ByteSlice};
1212

13-
use git_quote::to_single_quoted;
14-
1513
use crate::{
1614
client::{self, git, ssh, MessageKind, RequestWriter, SetServiceResponse, WriteMode},
1715
Protocol, Service,
@@ -215,13 +213,13 @@ impl client::Transport for SpawnProcessOnDemand {
215213
};
216214
cmd.stdin = Stdio::piped();
217215
cmd.stdout = Stdio::piped();
218-
if self.ssh_cmd.is_some() {
216+
let repo_path = if self.ssh_cmd.is_some() {
219217
cmd.args.push(service.as_str().into());
220-
cmd.args
221-
.push(to_single_quoted(self.path.as_ref()).to_os_str_lossy().into_owned());
218+
git_quote::single(self.path.as_ref()).to_os_str_lossy().into_owned()
222219
} else {
223-
cmd.args.push(self.path.to_os_str_lossy().into_owned());
224-
}
220+
self.path.to_os_str_lossy().into_owned()
221+
};
222+
cmd.args.push(repo_path);
225223

226224
let mut cmd = std::process::Command::from(cmd);
227225
for env_to_remove in ENV_VARS_TO_REMOVE {

0 commit comments

Comments
 (0)