use crate::{color::*, error::*, file::*, io::*, marshalling::*, metadata::*, reconcile::*};
use camino::Utf8Path;
use std::{
collections::{HashMap, HashSet},
fmt,
fs::File,
fs::remove_file,
str::FromStr,
time::SystemTime,
};
pub struct PkgIdxReconciler<'a> {
current: HashMap<Arch, CurrentPkgIdx<'a>>,
target: HashMap<Arch, TargetPkgIdx>,
}
pub struct CurrentPkgIdx<'a> {
mtime: SystemTime,
path: &'a Utf8Path,
pkgids: HashSet<PkgId>,
}
pub struct TargetPkgIdx {
mtime: SystemTime,
pkgids: HashSet<PkgId>,
}
pub struct PkgIdxRecArgs<'a> {
pub dir: &'a Utf8Path,
pub privkey: &'a PrivKey,
pub pubkeys: &'a PublicKeys,
}
impl<'a> PkgIdxReconciler<'a> {
pub fn new(
bbuilds: &'a [(SystemTime, &Utf8Path, Bbuild)],
pkgidxs: &'a [(SystemTime, &Utf8Path, PkgIdx)],
conf: &BptConf,
) -> Result<Self, Err> {
let mut current = HashMap::new();
for (mtime, path, pkgidx) in pkgidxs {
let arch = path
.file_stem()
.map(Arch::from_str)
.ok_or_else(|| Err::FilenameStemArch(path.to_string()))?
.map_err(|_| Err::FilenameStemArch(path.to_string()))?;
let pkgids = pkgidx.pkgids().cloned().collect();
current.insert(
arch,
CurrentPkgIdx {
mtime: *mtime,
path,
pkgids,
},
);
}
let mut target = HashMap::new();
for arch in &conf.make_repo.archs {
let mut pkgids = HashSet::new();
let mut newest_mtime = SystemTime::UNIX_EPOCH;
for (mtime, _, bbuild) in bbuilds.iter() {
if !bbuild.pkginfo().makearchs.as_slice().contains(arch) && arch != &Arch::bbuild {
continue;
}
pkgids.insert(bbuild.pkgid().with_arch(*arch));
if *mtime > newest_mtime {
newest_mtime = *mtime;
}
}
target.insert(
*arch,
TargetPkgIdx {
mtime: newest_mtime,
pkgids,
},
);
}
Ok(Self { current, target })
}
}
impl<'a> Reconciler<'a> for PkgIdxReconciler<'a> {
type Key = Arch;
type Current = CurrentPkgIdx<'a>;
type Target = TargetPkgIdx;
type ApplyArgs = PkgIdxRecArgs<'a>;
fn cmp(_key: &Self::Key, current: &Self::Current, target: &Self::Target) -> std::cmp::Ordering {
if current.mtime < target.mtime || current.pkgids != target.pkgids {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Equal
}
}
fn current(&self) -> &HashMap<Self::Key, Self::Current> {
&self.current
}
fn target(&self) -> &HashMap<Self::Key, Self::Target> {
&self.target
}
fn create(arch: &Self::Key, target: &Self::Target, args: &Self::ApplyArgs) -> Result<(), Err> {
let pkginfos = collect_pkginfos(&target.pkgids, args.dir, args.pubkeys)?;
let pkgidx = PkgIdx::from_pkginfos(&pkginfos, args.dir, args.privkey)?;
pkgidx.link(&args.dir.join(format!("{arch}.pkgidx")))
}
fn remove(
_arch: &Self::Key,
current: &Self::Current,
_args: &Self::ApplyArgs,
) -> Result<(), Err> {
remove_file(current.path).map_err(|e| Err::Remove(current.path.to_string(), e))
}
fn upgrade(
_arch: &Self::Key,
current: &Self::Current,
target: &Self::Target,
args: &Self::ApplyArgs,
) -> Result<(), Err> {
let pkginfos = collect_pkginfos(&target.pkgids, args.dir, args.pubkeys)?;
let pkgidx = PkgIdx::from_pkginfos(&pkginfos, args.dir, args.privkey)?;
if let Err(e) = remove_file(current.path) {
if e.kind() != std::io::ErrorKind::NotFound {
return Err(Err::Remove(current.path.to_string(), e));
}
}
pkgidx.link(current.path)
}
fn create_desc(
arch: &Self::Key,
_target: &Self::Target,
f: &mut fmt::Formatter<'_>,
) -> std::fmt::Result {
writeln!(
f,
"{}Create {}{}{}.{}pkgidx{}",
Color::Create,
Color::Arch,
arch,
Color::Glue,
Color::File,
Color::Default,
)
}
fn remove_desc(
arch: &Self::Key,
_current: &Self::Current,
f: &mut fmt::Formatter<'_>,
) -> std::fmt::Result {
writeln!(
f,
"{}Remove {}{}{}.{}pkgidx{}",
Color::Remove,
Color::Arch,
arch,
Color::Glue,
Color::File,
Color::Default,
)
}
fn upgrade_desc(
arch: &Self::Key,
_current: &Self::Current,
_target: &Self::Target,
f: &mut fmt::Formatter<'_>,
) -> std::fmt::Result {
writeln!(
f,
"{}Update {}{}{}.{}pkgidx{}",
Color::Upgrade,
Color::Arch,
arch,
Color::Glue,
Color::File,
Color::Default,
)
}
}
fn collect_pkginfos(
pkgids: &HashSet<PkgId>,
dir: &Utf8Path,
pubkeys: &PublicKeys,
) -> Result<Vec<PkgInfo>, Err> {
let mut pkginfos = Vec::new();
for path in dir.readdir()? {
match path.extension() {
Some("bpt") => {
let file = File::open_ro(&path)?;
let bpt = Bpt::from_file(file, pubkeys).loc(&path)?;
if pkgids.contains(bpt.pkgid()) {
let mut pkginfo = bpt.pkginfo().clone();
pkginfo.repopath = RepoPath::from_path(&path)?;
pkginfos.push(pkginfo);
}
}
Some("bbuild") => {
let file = File::open_ro(&path)?;
let bbuild = Bbuild::from_file(file, pubkeys, None).loc(&path)?;
if pkgids.contains(bbuild.pkgid()) {
let mut pkginfo = bbuild.pkginfo().clone();
pkginfo.repopath = RepoPath::from_path(&path)?;
pkginfos.push(pkginfo);
}
}
_ => continue,
}
}
pkginfos.sort_by_key(|pkginfo| pkginfo.pkgid.clone());
Ok(pkginfos)
}