| // 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: &str) -> Result<Vec<String>, String> { | |
| let file = File::open(path).map_err(|e| e.to_string()).map_err(|err| { | |
| format!( | |
| "{} reading path: {:?}, current directory: {:?}", | |
| err, | |
| path, | |
| std::env::current_dir() | |
| ) | |
| })?; | |
| 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); | |
| } | |
| } |