History

feat!: `ansi_c::unquote()` returns the amount of consumed bytes. (#301)

That way it's possible to continue parsing past the quoted string.

Sebastian Thiel committed 4 years ago

Commit a052d791 parent 22c776b2 files · +30 −19Browse files

Unified · Split

2 files
  1. git-quote/src/ansi_c.rs +11 −5
  2. git-quote/tests/quote.rs +19 −14
git-quote/src/ansi_c.rs+11−5
@@ -28,23 +28,25 @@ use std::{borrow::Cow, io::Read}; · 27 unmodified lines
2828
2929 use bstr::{BStr, BString, ByteSlice};
3030
31−/// Unquote the given ansi-c quoted `input` string.
31+/// Unquote the given ansi-c quoted `input` string, returning it and all of the consumed bytes.
3232 ///
3333 /// The `input` is returned unaltered if it doesn't start with a `"` character to indicate
34−/// quotation, otherwise a new unqoted string will always be allocated.
34+/// quotation, otherwise a new unquoted string will always be allocated.
35+/// The amount of consumed bytes allow to pass strings that start with a quote, and skip all quoted text for additional processing
3536 ///
3637 /// See [the tests][tests] for quotation examples.
3738 ///
3839 /// [tests]: https://github.com/Byron/gitoxide/blob/e355b4ad133075152312816816af5ce72cf79cff/git-odb/src/alternate/unquote.rs#L110-L118
39−pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> {
40+pub fn undo(input: &BStr) -> Result<(Cow<'_, BStr>, usize), undo::Error> {
4041 if !input.starts_with(b"\"") {
41− return Ok(input.into());
42+ return Ok((input.into(), input.len()));
4243 }
4344 if input.len() < 2 {
4445 return Err(undo::Error::new("Input must be surrounded by double quotes", input));
4546 }
4647 let original = input.as_bstr();
4748 let mut input = &input[1..];
49+ let mut consumed = 1;
4850 let mut out = BString::default();
4951 fn consume_one_past(input: &mut &BStr, position: usize) -> Result<u8, undo::Error> {
5052 *input = input
@@ -59,10 +61,12 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 8 unmodified lines
5961 match input.find_byteset(b"\"\\") {
6062 Some(position) => {
6163 out.extend_from_slice(&input[..position]);
64+ consumed += position + 1;
6265 match input[position] {
6366 b'"' => break,
6467 b'\\' => {
6568 let next = consume_one_past(&mut input, position)?;
69+ consumed += 1;
6670 match next {
6771 b'n' => out.push(b'\n'),
6872 b'r' => out.push(b'\r'),
@@ -88,6 +92,7 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 19 unmodified lines
8892 let byte = btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?;
8993 out.push(byte);
9094 input = &input[2..];
95+ consumed += 2;
9196 }
9297 _ => {
9398 return Err(undo::Error::UnsupportedEscapeByte {
@@ -102,9 +107,10 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 8 unmodified lines
102107 }
103108 None => {
104109 out.extend_from_slice(input);
110+ consumed += input.len();
105111 break;
106112 }
107113 }
108114 }
109− Ok(out.into())
115+ Ok((out.into(), consumed))
110116 }
git-quote/tests/quote.rs+19−14
@@ -4,40 +4,45 @@ mod ansi_c { · 3 unmodified lines
44 use git_quote::ansi_c;
55
66 macro_rules! test {
7− ($name:ident, $input:literal, $expected:literal) => {
7+ ($name:ident, $input:literal, $expected:literal, $consumed:literal) => {
88 #[test]
99 fn $name() {
1010 assert_eq!(
1111 ansi_c::undo($input.as_bytes().as_bstr()).expect("valid input"),
12− std::borrow::Cow::Borrowed($expected.as_bytes().as_bstr())
12+ (
13+ std::borrow::Cow::Borrowed($expected.as_bytes().as_bstr()),
14+ $consumed
15+ )
1316 );
1417 }
1518 };
1619 }
1720
18− test!(unquoted_remains_unchanged, "hello", "hello");
19− test!(empty_surrounded_by_quotes, "\"\"", "");
20− test!(surrounded_only_by_quotes, "\"hello\"", "hello");
21− test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t");
22− test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b");
23− test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#);
21+ test!(unquoted_remains_unchanged, "hello", "hello", 5);
22+ test!(empty_surrounded_by_quotes, "\"\"", "", 2);
23+ test!(surrounded_only_by_quotes, "\"hello\"", "hello", 7);
24+ test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t", 8);
25+ test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b", 10);
26+ test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#, 6);
2427 test!(
2528 unicode_byte_escapes_by_number,
2629 r#""\346\277\261\351\207\216\t\347\264\224""#,
27− "濱野\t純"
30+ "濱野\t純",
31+ 40
2832 );
2933 test!(
3034 exclamation_and_tilde_survive_an_escape_with_double_escaping,
3135 r#""\\!\\#hello there/file.ext""#,
32− r"\!\#hello there/file.ext"
36+ r"\!\#hello there/file.ext",
37+ 28
3338 );
3439
3540 #[test]
3641 fn out_of_quote_characters_can_be_passed_and_will_not_be_consumed() {
37− assert_eq!(
38− ansi_c::undo(br#""hello there" out of quote"#.as_bstr()).expect("valid input"),
39− std::borrow::Cow::Borrowed(b"hello there".as_bstr())
40− );
42+ let input = br#""hello there" out of quote"#.as_bstr();
43+ let (unquoted, consumed) = ansi_c::undo(input).expect("valid input");
44+ assert_eq!(unquoted, std::borrow::Cow::Borrowed(b"hello there".as_bstr()));
45+ assert_eq!(&input[consumed..], " out of quote");
4146 }
4247 }
4348 }