1 // Copyright 2020 The Bazel Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use std::fs::File;
16 use std::io::{BufRead, BufReader, Read};
17
read_file_to_array(path: &str) -> Result<Vec<String>, String>18 pub(crate) fn read_file_to_array(path: &str) -> Result<Vec<String>, String> {
19 let file = File::open(path).map_err(|e| e.to_string()).map_err(|err| {
20 format!(
21 "{} reading path: {:?}, current directory: {:?}",
22 err,
23 path,
24 std::env::current_dir()
25 )
26 })?;
27
28 read_to_array(file)
29 }
30
read_stamp_status_to_array(path: String) -> Result<Vec<(String, String)>, String>31 pub(crate) fn read_stamp_status_to_array(path: String) -> Result<Vec<(String, String)>, String> {
32 let file = File::open(path).map_err(|e| e.to_string())?;
33 stamp_status_to_array(file)
34 }
35
read_to_array(reader: impl Read) -> Result<Vec<String>, String>36 fn read_to_array(reader: impl Read) -> Result<Vec<String>, String> {
37 let reader = BufReader::new(reader);
38 let mut ret = vec![];
39 let mut escaped_line = String::new();
40 for l in reader.lines() {
41 let line = l.map_err(|e| e.to_string())?;
42 if line.is_empty() {
43 continue;
44 }
45 // a \ at the end of a line allows us to escape the new line break,
46 // \\ yields a single \, so \\\ translates to a single \ and a new line
47 // escape
48 let end_backslash_count = line.chars().rev().take_while(|&c| c == '\\').count();
49 // a 0 or even number of backslashes do not lead to a new line escape
50 let escape = end_backslash_count % 2 == 1;
51 // remove backslashes and add back two for every one
52 let l = line.trim_end_matches('\\');
53 escaped_line.push_str(l);
54 for _ in 0..end_backslash_count / 2 {
55 escaped_line.push('\\');
56 }
57 if escape {
58 // we add a newline as we expect a line after this
59 escaped_line.push('\n');
60 } else {
61 ret.push(escaped_line);
62 escaped_line = String::new();
63 }
64 }
65 Ok(ret)
66 }
67
stamp_status_to_array(reader: impl Read) -> Result<Vec<(String, String)>, String>68 fn stamp_status_to_array(reader: impl Read) -> Result<Vec<(String, String)>, String> {
69 let escaped_lines = read_to_array(reader)?;
70 escaped_lines
71 .into_iter()
72 .map(|l| {
73 let (s1, s2) = l
74 .split_once(' ')
75 .ok_or_else(|| format!("wrong workspace status file format for \"{l}\""))?;
76 Ok((s1.to_owned(), s2.to_owned()))
77 })
78 .collect()
79 }
80
81 #[cfg(test)]
82 mod test {
83 use super::*;
84
85 #[test]
test_read_to_array()86 fn test_read_to_array() {
87 let input = r"some escaped \\\
88 string
89 with other lines"
90 .to_owned();
91 let expected = vec![
92 r"some escaped \
93 string",
94 "with other lines",
95 ];
96 let got = read_to_array(input.as_bytes()).unwrap();
97 assert_eq!(expected, got);
98 }
99
100 #[test]
test_stamp_status_to_array()101 fn test_stamp_status_to_array() {
102 let lines = "aaa bbb\\\nvvv\nccc ddd\neee fff";
103 let got = stamp_status_to_array(lines.as_bytes()).unwrap();
104 let expected = vec![
105 ("aaa".to_owned(), "bbb\nvvv".to_owned()),
106 ("ccc".to_owned(), "ddd".to_owned()),
107 ("eee".to_owned(), "fff".to_owned()),
108 ];
109 assert_eq!(expected, got);
110 }
111 }
112