Skip to content

Commit

Permalink
Implement "outdated" check and introduce "compatible" version
Browse files Browse the repository at this point in the history
Package search only returns a boolean depending on whether a package
with a correct version exists in Debian (or NEW). This isn't sufficient
to handle the cases where the package exists, but is so outdated that
the version in Debian is likely incompatible with the crate we're
inspecting.

Moreover, this doesn't differentiate between exact version matches and
semver'd matches (e.g. when the Debian version is older but has the same
major -- and, if needed, minor -- as the version found on crates.io). In
order to deal with the latter case, this change introduces the notion of
"compatible" version: it represents crates already packaged in Debian
but not on the latest version, although it *should* still be compatible
with the requirements.

The "compatible" and "outdated" information are then propagated, as well
as the Debian package version, through a new `db::PkgInfo` structure
used as the return type for the `db::search*` functions. That way, this
additional information can easily be processed by other modules and
ultimately be reported to the user.
  • Loading branch information
a-wai committed Nov 2, 2023
1 parent 57937da commit 18897d7
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 31 deletions.
81 changes: 57 additions & 24 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,26 @@ use std::time::{Duration, SystemTime};
const POSTGRES: &str = "postgresql://udd-mirror:udd-mirror@udd-mirror.debian.net/udd";
const CACHE_EXPIRE: Duration = Duration::from_secs(90 * 60);

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum PkgStatus {
NotFound,
Outdated,
Compatible,
Found,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PkgInfo {
pub status: PkgStatus,
pub version: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CacheEntry {
pub from: SystemTime,
pub found: bool,
pub info: PkgInfo,
}

// TODO: also use this for outdated check(?)
fn is_compatible(debversion: &str, crateversion: &VersionReq) -> Result<bool, Error> {
let debversion = debversion.replace('~', "-");
let debversion = Version::parse(&debversion)?;
Expand Down Expand Up @@ -56,20 +69,28 @@ impl Connection {
target: &str,
package: &str,
version: &Version,
) -> Result<Option<bool>, Error> {
) -> Result<Option<PkgInfo>, Error> {
let path = self.cache_path(target, package, version);

if !path.exists() {
return Ok(None);
}

let buf = fs::read(path)?;
let cache: CacheEntry = serde_json::from_slice(&buf)?;
let buf = fs::read(&path)?;
// If the cache entry can't be deserialized, it's probably using an old
// entry format, so let's discard it
let cache: CacheEntry = match serde_json::from_slice(&buf) {
Ok(e) => e,
_ => {
fs::remove_file(path)?;
return Ok(None);
}
};

if SystemTime::now().duration_since(cache.from)? > CACHE_EXPIRE {
Ok(None)
} else {
Ok(Some(cache.found))
Ok(Some(cache.info))
}
}

Expand All @@ -78,57 +99,61 @@ impl Connection {
target: &str,
package: &str,
version: &Version,
found: bool,
info: &PkgInfo,
) -> Result<(), Error> {
let cache = CacheEntry {
from: SystemTime::now(),
found,
info: info.clone(),
};
let buf = serde_json::to_vec(&cache)?;
fs::write(self.cache_path(target, package, version), buf)?;
Ok(())
}

pub fn search(&mut self, package: &str, version: &Version) -> Result<bool, Error> {
if let Some(found) = self.check_cache("sid", package, version)? {
return Ok(found);
pub fn search(&mut self, package: &str, version: &Version) -> Result<PkgInfo, Error> {
if let Some(info) = self.check_cache("sid", package, version)? {
return Ok(info);
}

// config.shell().status("Querying", format!("sid: {}", package))?;
info!("Querying -> sid: {}", package);
let found = self.search_generic(
let info = self.search_generic(
"SELECT version::text FROM sources WHERE source in ($1, $2) AND release='sid';",
package,
version,
)?;

self.write_cache("sid", package, version, found)?;
Ok(found)
self.write_cache("sid", package, version, &info)?;
Ok(info)
}

pub fn search_new(&mut self, package: &str, version: &Version) -> Result<bool, Error> {
if let Some(found) = self.check_cache("new", package, version)? {
return Ok(found);
pub fn search_new(&mut self, package: &str, version: &Version) -> Result<PkgInfo, Error> {
if let Some(info) = self.check_cache("new", package, version)? {
return Ok(info);
}

// config.shell().status("Querying", format!("new: {}", package))?;
info!("Querying -> new: {}", package);
let found = self.search_generic(
let info = self.search_generic(
"SELECT version::text FROM new_sources WHERE source in ($1, $2);",
package,
version,
)?;

self.write_cache("new", package, version, found)?;
Ok(found)
self.write_cache("new", package, version, &info)?;
Ok(info)
}

pub fn search_generic(
&mut self,
query: &str,
package: &str,
version: &Version,
) -> Result<bool, Error> {
) -> Result<PkgInfo, Error> {
let mut info = PkgInfo {
status: PkgStatus::NotFound,
version: String::new(),
};
let package = package.replace('_', "-");
let semver_version = if version.major == 0 {
if version.minor == 0 {
Expand Down Expand Up @@ -160,12 +185,20 @@ impl Connection {

// println!("{:?} ({:?}) => {:?}", debversion, version, is_compatible(debversion, version)?);

if is_compatible(debversion, &version)? || is_compatible(debversion, &semver_version)? {
return Ok(true);
if is_compatible(debversion, &version)? {
info.version = debversion.to_string();
info.status = PkgStatus::Found;
return Ok(info);
} else if is_compatible(debversion, &semver_version)? {
info.version = debversion.to_string();
info.status = PkgStatus::Compatible;
} else if info.status == PkgStatus::NotFound {
info.version = debversion.to_string();
info.status = PkgStatus::Outdated;
}
}

Ok(false)
Ok(info)
}
}

Expand Down
24 changes: 20 additions & 4 deletions src/debian.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::db::Connection;
use crate::db::{Connection, PkgStatus};
use crate::errors::*;
use crate::graph::Graph;
use cargo_metadata::{Package, PackageId, Source};
Expand Down Expand Up @@ -52,19 +52,35 @@ pub struct DebianInfo {
pub in_unstable: bool,
pub in_new: bool,
pub outdated: bool,
pub compatible: bool,
pub version: String,
}

fn run_task(db: &mut Connection, pkg: Pkg) -> Result<DebianInfo> {
let mut deb = DebianInfo {
in_unstable: false,
in_new: false,
outdated: false,
compatible: false,
version: String::new(),
};

if db.search(&pkg.name, &pkg.version).unwrap() {
let mut info = db.search(&pkg.name, &pkg.version).unwrap();
if info.status == PkgStatus::NotFound {
info = db.search_new(&pkg.name, &pkg.version).unwrap();
if info.status != PkgStatus::NotFound {
deb.in_new = true;
deb.version = info.version;
}
} else {
deb.in_unstable = true;
} else if db.search_new(&pkg.name, &pkg.version).unwrap() {
deb.in_new = true;
deb.version = info.version;
}

match info.status {
PkgStatus::Outdated => deb.outdated = true,
PkgStatus::Compatible => deb.compatible = true,
_ => (),
}

Ok(deb)
Expand Down
24 changes: 21 additions & 3 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,29 @@ impl<'a> fmt::Display for Display<'a> {
let pkg = format!("{} v{}", self.package.name, self.package.version);
if let Some(deb) = &self.package.debinfo {
if deb.in_unstable {
write!(fmt, "{} (in debian)", pkg.green())?;
if deb.compatible {
write!(
fmt,
"{} ({} in debian)",
pkg.green(),
deb.version.yellow()
)?;
} else {
write!(fmt, "{} (in debian)", pkg.green())?;
}
} else if deb.in_new {
write!(fmt, "{} (in debian NEW queue)", pkg.blue())?;
if deb.compatible {
write!(
fmt,
"{} ({} in debian NEW queue)",
pkg.blue(),
deb.version.yellow()
)?;
} else {
write!(fmt, "{} (in debian NEW queue)", pkg.blue())?;
}
} else if deb.outdated {
write!(fmt, "{} (outdated)", pkg.yellow())?;
write!(fmt, "{} (outdated, {})", pkg.red(), deb.version)?;
} else {
write!(fmt, "{pkg}")?;
}
Expand Down

0 comments on commit 18897d7

Please sign in to comment.