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