Brian Silverman | cc09f18 | 2022-03-09 15:40:20 -0800 | [diff] [blame^] | 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 | |
| 18 | pub(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 | |
| 23 | pub(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 | |
| 28 | fn 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 | |
| 60 | fn 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)] |
| 74 | mod test { |
| 75 | use super::*; |
| 76 | |
| 77 | #[test] |
| 78 | fn test_read_to_array() { |
| 79 | let input = r#"some escaped \\\ |
| 80 | string |
| 81 | with other lines"# |
| 82 | .to_owned(); |
| 83 | let expected = vec![ |
| 84 | r#"some escaped \ |
| 85 | string"#, |
| 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 | } |