blob: 7bab19ef79c58ae77286caa2f0f663a4664f02bc [file] [log] [blame]
// 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);
}
}