use {
crate::error::Result,
futures::{AsyncBufRead, AsyncBufReadExt},
pin_project::pin_project,
std::{
collections::{BTreeMap, BTreeSet},
io::{BufRead, Write},
},
};
#[derive(Clone, Debug, Default)]
pub struct ContentsFile {
paths: BTreeMap<String, BTreeSet<String>>,
packages: BTreeMap<String, BTreeSet<String>>,
}
impl ContentsFile {
fn parse_and_add_line(&mut self, line: &str) -> Result<()> {
let words = line.split_ascii_whitespace().collect::<Vec<_>>();
if words.len() != 2 {
return Ok(());
}
let path = words[0];
let packages = words[1];
for package in packages.split(',') {
self.paths
.entry(path.to_string())
.or_default()
.insert(package.to_string());
self.packages
.entry(package.to_string())
.or_default()
.insert(path.to_string());
}
Ok(())
}
pub fn add_package_path(&mut self, path: String, package: String) {
self.paths
.entry(path.clone())
.or_default()
.insert(package.clone());
self.packages.entry(package).or_default().insert(path);
}
pub fn packages_with_path(&self, path: &str) -> Box<dyn Iterator<Item = &str> + '_> {
if let Some(packages) = self.paths.get(path) {
Box::new(packages.iter().map(|x| x.as_str()))
} else {
Box::new(std::iter::empty())
}
}
pub fn package_paths(&self, package: &str) -> Box<dyn Iterator<Item = &str> + '_> {
if let Some(paths) = self.packages.get(package) {
Box::new(paths.iter().map(|x| x.as_str()))
} else {
Box::new(std::iter::empty())
}
}
pub fn as_lines(&self) -> impl Iterator<Item = String> + '_ {
self.paths.iter().map(|(path, packages)| {
let packages = packages.iter().map(|s| s.as_str()).collect::<Vec<_>>();
format!("{} {}\n", path, packages.join(",'"))
})
}
pub fn write_to(&self, writer: &mut impl Write) -> Result<usize> {
let mut bytes_count = 0;
for line in self.as_lines() {
writer.write_all(line.as_bytes())?;
bytes_count += line.as_bytes().len();
}
Ok(bytes_count)
}
}
#[derive(Clone, Debug)]
pub struct ContentsFileReader<R> {
reader: R,
contents: ContentsFile,
}
impl<R: BufRead> ContentsFileReader<R> {
pub fn new(reader: R) -> Self {
Self {
reader,
contents: ContentsFile::default(),
}
}
pub fn into_inner(self) -> R {
self.reader
}
pub fn read_all(&mut self) -> Result<usize> {
let mut bytes_read = 0;
while let Ok(read_size) = self.read_line() {
if read_size == 0 {
break;
}
bytes_read += read_size;
}
Ok(bytes_read)
}
pub fn read_line(&mut self) -> Result<usize> {
let mut line = String::new();
let read_size = self.reader.read_line(&mut line)?;
if read_size != 0 {
self.contents.parse_and_add_line(&line)?;
}
Ok(read_size)
}
pub fn consume(self) -> (ContentsFile, R) {
(self.contents, self.reader)
}
}
#[pin_project]
pub struct ContentsFileAsyncReader<R> {
#[pin]
reader: R,
contents: ContentsFile,
}
impl<R> ContentsFileAsyncReader<R>
where
R: AsyncBufRead + Unpin,
{
pub fn new(reader: R) -> Self {
Self {
reader,
contents: ContentsFile::default(),
}
}
pub fn into_inner(self) -> R {
self.reader
}
pub async fn read_all(&mut self) -> Result<usize> {
let mut bytes_read = 0;
while let Ok(read_size) = self.read_line().await {
if read_size == 0 {
break;
}
bytes_read += read_size;
}
Ok(bytes_read)
}
pub async fn read_line(&mut self) -> Result<usize> {
let mut line = String::new();
let read_size = self.reader.read_line(&mut line).await?;
if read_size != 0 {
self.contents.parse_and_add_line(&line)?;
}
Ok(read_size)
}
pub fn consume(self) -> (ContentsFile, R) {
(self.contents, self.reader)
}
}