blob: 4b3d6bb17c701587afd5e0f3818f77c83444cb3d [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001// 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
15use std::fs::File;
16use std::io::{BufRead, BufReader, Read};
17
18pub(crate) fn read_file_to_array(path: String) -> Result<Vec<String>, String> {
19 let file = File::open(path).map_err(|e| e.to_string())?;
20 read_to_array(file)
21}
22
23pub(crate) fn read_stamp_status_to_array(path: String) -> Result<Vec<(String, String)>, String> {
24 let file = File::open(path).map_err(|e| e.to_string())?;
25 stamp_status_to_array(file)
26}
27
28fn read_to_array(reader: impl Read) -> Result<Vec<String>, String> {
29 let reader = BufReader::new(reader);
30 let mut ret = vec![];
31 let mut escaped_line = String::new();
32 for l in reader.lines() {
33 let line = l.map_err(|e| e.to_string())?;
34 if line.is_empty() {
35 continue;
36 }
37 // a \ at the end of a line allows us to escape the new line break,
38 // \\ yields a single \, so \\\ translates to a single \ and a new line
39 // escape
40 let end_backslash_count = line.chars().rev().take_while(|&c| c == '\\').count();
41 // a 0 or even number of backslashes do not lead to a new line escape
42 let escape = end_backslash_count % 2 == 1;
43 // remove backslashes and add back two for every one
44 let l = line.trim_end_matches('\\');
45 escaped_line.push_str(l);
46 for _ in 0..end_backslash_count / 2 {
47 escaped_line.push('\\');
48 }
49 if escape {
50 // we add a newline as we expect a line after this
51 escaped_line.push('\n');
52 } else {
53 ret.push(escaped_line);
54 escaped_line = String::new();
55 }
56 }
57 Ok(ret)
58}
59
60fn stamp_status_to_array(reader: impl Read) -> Result<Vec<(String, String)>, String> {
61 let escaped_lines = read_to_array(reader)?;
62 escaped_lines
63 .into_iter()
64 .map(|l| {
65 let (s1, s2) = l
66 .split_once(' ')
67 .ok_or_else(|| format!("wrong workspace status file format for \"{}\"", l))?;
68 Ok((s1.to_owned(), s2.to_owned()))
69 })
70 .collect()
71}
72
73#[cfg(test)]
74mod test {
75 use super::*;
76
77 #[test]
78 fn test_read_to_array() {
79 let input = r#"some escaped \\\
80string
81with other lines"#
82 .to_owned();
83 let expected = vec![
84 r#"some escaped \
85string"#,
86 "with other lines",
87 ];
88 let got = read_to_array(input.as_bytes()).unwrap();
89 assert_eq!(expected, got);
90 }
91
92 #[test]
93 fn test_stamp_status_to_array() {
94 let lines = "aaa bbb\\\nvvv\nccc ddd\neee fff";
95 let got = stamp_status_to_array(lines.as_bytes()).unwrap();
96 let expected = vec![
97 ("aaa".to_owned(), "bbb\nvvv".to_owned()),
98 ("ccc".to_owned(), "ddd".to_owned()),
99 ("eee".to_owned(), "fff".to_owned()),
100 ];
101 assert_eq!(expected, got);
102 }
103}