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
28 28
29use bstr::{BStr, BString, ByteSlice};29use bstr::{BStr, BString, ByteSlice};
30 30
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.
32///32///
33/// The `input` is returned unaltered if it doesn't start with a `"` character to indicate33/// 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
35///36///
36/// See [the tests][tests] for quotation examples.37/// See [the tests][tests] for quotation examples.
37///38///
38/// [tests]: https://github.com/Byron/gitoxide/blob/e355b4ad133075152312816816af5ce72cf79cff/git-odb/src/alternate/unquote.rs#L110-L11839/// [tests]: https://github.com/Byron/gitoxide/blob/e355b4ad133075152312816816af5ce72cf79cff/git-odb/src/alternate/unquote.rs#L110-L118
39pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> {40pub fn undo(input: &BStr) -> Result<(Cow<'_, BStr>, usize), undo::Error> {
40 if !input.starts_with(b"\"") {41 if !input.starts_with(b"\"") {
41 return Ok(input.into());42 return Ok((input.into(), input.len()));
42 }43 }
43 if input.len() < 2 {44 if input.len() < 2 {
44 return Err(undo::Error::new("Input must be surrounded by double quotes", input));45 return Err(undo::Error::new("Input must be surrounded by double quotes", input));
45 }46 }
46 let original = input.as_bstr();47 let original = input.as_bstr();
47 let mut input = &input[1..];48 let mut input = &input[1..];
49 let mut consumed = 1;
48 let mut out = BString::default();50 let mut out = BString::default();
49 fn consume_one_past(input: &mut &BStr, position: usize) -> Result<u8, undo::Error> {51 fn consume_one_past(input: &mut &BStr, position: usize) -> Result<u8, undo::Error> {
50 *input = input52 *input = input
@@ -59,10 +61,12 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 8 unmodified lines
59 match input.find_byteset(b"\"\\") {61 match input.find_byteset(b"\"\\") {
60 Some(position) => {62 Some(position) => {
61 out.extend_from_slice(&input[..position]);63 out.extend_from_slice(&input[..position]);
64 consumed += position + 1;
62 match input[position] {65 match input[position] {
63 b'"' => break,66 b'"' => break,
64 b'\\' => {67 b'\\' => {
65 let next = consume_one_past(&mut input, position)?;68 let next = consume_one_past(&mut input, position)?;
69 consumed += 1;
66 match next {70 match next {
67 b'n' => out.push(b'\n'),71 b'n' => out.push(b'\n'),
68 b'r' => out.push(b'\r'),72 b'r' => out.push(b'\r'),
@@ -88,6 +92,7 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 19 unmodified lines
88 let byte = btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?;92 let byte = btoi::btou_radix(&buf, 8).map_err(|e| undo::Error::new(e, original))?;
89 out.push(byte);93 out.push(byte);
90 input = &input[2..];94 input = &input[2..];
95 consumed += 2;
91 }96 }
92 _ => {97 _ => {
93 return Err(undo::Error::UnsupportedEscapeByte {98 return Err(undo::Error::UnsupportedEscapeByte {
@@ -102,9 +107,10 @@ pub fn undo(input: &BStr) -> Result<Cow<'_, BStr>, undo::Error> { · 8 unmodified lines
102 }107 }
103 None => {108 None => {
104 out.extend_from_slice(input);109 out.extend_from_slice(input);
110 consumed += input.len();
105 break;111 break;
106 }112 }
107 }113 }
108 }114 }
109 Ok(out.into())115 Ok((out.into(), consumed))
110}116}
git-quote/tests/quote.rs+19−14
@@ -4,40 +4,45 @@ mod ansi_c { · 3 unmodified lines
4 use git_quote::ansi_c;4 use git_quote::ansi_c;
5 5
6 macro_rules! test {6 macro_rules! test {
7 ($name:ident, $input:literal, $expected:literal) => {7 ($name:ident, $input:literal, $expected:literal, $consumed:literal) => {
8 #[test]8 #[test]
9 fn $name() {9 fn $name() {
10 assert_eq!(10 assert_eq!(
11 ansi_c::undo($input.as_bytes().as_bstr()).expect("valid input"),11 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 )
13 );16 );
14 }17 }
15 };18 };
16 }19 }
17 20
18 test!(unquoted_remains_unchanged, "hello", "hello");21 test!(unquoted_remains_unchanged, "hello", "hello", 5);
19 test!(empty_surrounded_by_quotes, "\"\"", "");22 test!(empty_surrounded_by_quotes, "\"\"", "", 2);
20 test!(surrounded_only_by_quotes, "\"hello\"", "hello");23 test!(surrounded_only_by_quotes, "\"hello\"", "hello", 7);
21 test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t");24 test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t", 8);
22 test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b");25 test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b", 10);
23 test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#);26 test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#, 6);
24 test!(27 test!(
25 unicode_byte_escapes_by_number,28 unicode_byte_escapes_by_number,
26 r#""\346\277\261\351\207\216\t\347\264\224""#,29 r#""\346\277\261\351\207\216\t\347\264\224""#,
27 "濱野\t純"30 "濱野\t純",
31 40
28 );32 );
29 test!(33 test!(
30 exclamation_and_tilde_survive_an_escape_with_double_escaping,34 exclamation_and_tilde_survive_an_escape_with_double_escaping,
31 r#""\\!\\#hello there/file.ext""#,35 r#""\\!\\#hello there/file.ext""#,
32 r"\!\#hello there/file.ext"36 r"\!\#hello there/file.ext",
37 28
33 );38 );
34 39
35 #[test]40 #[test]
36 fn out_of_quote_characters_can_be_passed_and_will_not_be_consumed() {41 fn out_of_quote_characters_can_be_passed_and_will_not_be_consumed() {
37 assert_eq!(42 let input = br#""hello there" out of quote"#.as_bstr();
38 ansi_c::undo(br#""hello there" out of quote"#.as_bstr()).expect("valid input"),43 let (unquoted, consumed) = ansi_c::undo(input).expect("valid input");
39 std::borrow::Cow::Borrowed(b"hello there".as_bstr())44 assert_eq!(unquoted, std::borrow::Cow::Borrowed(b"hello there".as_bstr()));
40 );45 assert_eq!(&input[consumed..], " out of quote");
41 }46 }
42 }47 }
43}48}