Squashed 'third_party/rules_rust/' content from commit bf59038ca

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/util/process_wrapper/util.rs b/util/process_wrapper/util.rs
new file mode 100644
index 0000000..4b3d6bb
--- /dev/null
+++ b/util/process_wrapper/util.rs
@@ -0,0 +1,103 @@
+// Copyright 2020 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fs::File;
+use std::io::{BufRead, BufReader, Read};
+
+pub(crate) fn read_file_to_array(path: String) -> Result<Vec<String>, String> {
+    let file = File::open(path).map_err(|e| e.to_string())?;
+    read_to_array(file)
+}
+
+pub(crate) fn read_stamp_status_to_array(path: String) -> Result<Vec<(String, String)>, String> {
+    let file = File::open(path).map_err(|e| e.to_string())?;
+    stamp_status_to_array(file)
+}
+
+fn read_to_array(reader: impl Read) -> Result<Vec<String>, String> {
+    let reader = BufReader::new(reader);
+    let mut ret = vec![];
+    let mut escaped_line = String::new();
+    for l in reader.lines() {
+        let line = l.map_err(|e| e.to_string())?;
+        if line.is_empty() {
+            continue;
+        }
+        // a \ at the end of a line allows us to escape the new line break,
+        // \\ yields a single \, so \\\ translates to a single \ and a new line
+        // escape
+        let end_backslash_count = line.chars().rev().take_while(|&c| c == '\\').count();
+        // a 0 or even number of backslashes do not lead to a new line escape
+        let escape = end_backslash_count % 2 == 1;
+        //  remove backslashes and add back two for every one
+        let l = line.trim_end_matches('\\');
+        escaped_line.push_str(l);
+        for _ in 0..end_backslash_count / 2 {
+            escaped_line.push('\\');
+        }
+        if escape {
+            // we add a newline as we expect a line after this
+            escaped_line.push('\n');
+        } else {
+            ret.push(escaped_line);
+            escaped_line = String::new();
+        }
+    }
+    Ok(ret)
+}
+
+fn stamp_status_to_array(reader: impl Read) -> Result<Vec<(String, String)>, String> {
+    let escaped_lines = read_to_array(reader)?;
+    escaped_lines
+        .into_iter()
+        .map(|l| {
+            let (s1, s2) = l
+                .split_once(' ')
+                .ok_or_else(|| format!("wrong workspace status file format for \"{}\"", l))?;
+            Ok((s1.to_owned(), s2.to_owned()))
+        })
+        .collect()
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_read_to_array() {
+        let input = r#"some escaped \\\
+string
+with other lines"#
+            .to_owned();
+        let expected = vec![
+            r#"some escaped \
+string"#,
+            "with other lines",
+        ];
+        let got = read_to_array(input.as_bytes()).unwrap();
+        assert_eq!(expected, got);
+    }
+
+    #[test]
+    fn test_stamp_status_to_array() {
+        let lines = "aaa bbb\\\nvvv\nccc ddd\neee fff";
+        let got = stamp_status_to_array(lines.as_bytes()).unwrap();
+        let expected = vec![
+            ("aaa".to_owned(), "bbb\nvvv".to_owned()),
+            ("ccc".to_owned(), "ddd".to_owned()),
+            ("eee".to_owned(), "fff".to_owned()),
+        ];
+        assert_eq!(expected, got);
+    }
+}