use crate::pkg::{Pkg, Pkgs};
use anyhow::anyhow;
use flate2::read::GzDecoder;
use std::{
fs::{self, File},
io::BufReader,
path::{Path, PathBuf},
str::FromStr,
};
use tar::Archive;
use xz2::read::XzDecoder;
#[derive(Eq, PartialEq)]
pub enum RepoDBKind {
Gzip,
Xz,
}
impl FromStr for RepoDBKind {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
match s {
"gz" => Ok(RepoDBKind::Gzip),
"xz" => Ok(RepoDBKind::Xz),
_ => Err(anyhow!(
"repository DB has unsupported file extension '{}'",
s
)),
}
}
}
pub struct RepoDB {
path: PathBuf,
kind: RepoDBKind,
}
impl RepoDB {
pub fn new<P>(db: P) -> anyhow::Result<RepoDB>
where
P: AsRef<Path>,
{
let err_msg = format!(
"cannot determine extension of repository DB '{:?}'",
db.as_ref().display()
);
let path = fs::canonicalize(db)?;
let kind = RepoDBKind::from_str(
path.extension()
.ok_or_else(|| anyhow!(err_msg.clone()))?
.to_str()
.ok_or_else(|| anyhow!(err_msg.clone()))?,
)?;
Ok(RepoDB { path, kind })
}
pub fn packages(&self) -> anyhow::Result<Pkgs> {
macro_rules! parse_archive {
($archive:expr , $pkgs:expr) => {
let mut pkg: Pkg;
for entry in ($archive.entries()?).flatten() {
if entry.path()?.file_name().unwrap().to_str().unwrap() == "desc" {
pkg = Pkg::parse(BufReader::new(entry))?;
$pkgs.add(pkg.name.clone(), pkg);
}
}
};
}
let mut pkgs = Pkgs::default();
let reader = BufReader::new(File::open(&self.path)?);
if self.kind == RepoDBKind::Gzip {
parse_archive!(Archive::new(GzDecoder::new(reader)), pkgs);
} else {
parse_archive!(Archive::new(XzDecoder::new(reader)), pkgs);
};
Ok(pkgs)
}
}