use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
pub fn read_file(path: impl AsRef<Path>) -> io::Result<String> {
let path = path.as_ref();
let mut f = File::open(path)?;
let is_gz = path.extension().and_then(|s| s.to_str()) == Some("gz");
if is_gz {
let mut buf = Vec::new();
f.read_to_end(&mut buf)?;
let mut d = flate2::read::GzDecoder::new(&buf[..]);
let mut s = String::new();
d.read_to_string(&mut s)?;
Ok(s)
} else {
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConcatEntry {
pub instance: String,
pub text: String,
}
pub fn split_concatenated(text: &str) -> Vec<ConcatEntry> {
let mut out = Vec::new();
let mut cur_name: Option<String> = None;
let mut cur_text = String::new();
for line in text.split_inclusive('\n') {
if let Some(rest) = line.strip_prefix("@01 ") {
if let Some(name) = cur_name.take() {
out.push(ConcatEntry {
instance: name,
text: std::mem::take(&mut cur_text),
});
}
let name = rest
.trim_end_matches('\n')
.trim_end_matches(|c: char| c == '=' || c.is_whitespace())
.trim()
.to_string();
cur_name = Some(name);
cur_text.push_str(line);
} else if cur_name.is_some() {
cur_text.push_str(line);
}
}
if let Some(name) = cur_name {
out.push(ConcatEntry {
instance: name,
text: cur_text,
});
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_finds_entries() {
let text = "preamble ignored\n\
@01 modified/foo.mps.gz ===========\n\
foo log line\n\
@05 7200\n\
@01 modified/bar.mps.gz ===========\n\
bar log line\n";
let entries = split_concatenated(text);
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].instance, "modified/foo.mps.gz");
assert!(entries[0].text.contains("foo log line"));
assert_eq!(entries[1].instance, "modified/bar.mps.gz");
assert!(entries[1].text.contains("bar log line"));
}
#[test]
fn split_empty_when_no_markers() {
assert!(split_concatenated("random text\n").is_empty());
}
}