From 7df435223cf90ccb7adb182547cb9981cfc37ea2 Mon Sep 17 00:00:00 2001 From: Kornel Date: Sun, 1 Sep 2019 13:44:09 +0100 Subject: Parse semver in db --- crate_db/Cargo.toml | 1 + crate_db/src/builddb.rs | 128 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 105 insertions(+), 24 deletions(-) (limited to 'crate_db') diff --git a/crate_db/Cargo.toml b/crate_db/Cargo.toml index 2789d89..c0ab88a 100644 --- a/crate_db/Cargo.toml +++ b/crate_db/Cargo.toml @@ -22,6 +22,7 @@ parking_lot = "0.9" rake = { git = "https://github.com/kornelski/rake-rs" } rmp-serde = "0.14" heck = "0.3.1" +semver = "0.9.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/crate_db/src/builddb.rs b/crate_db/src/builddb.rs index fdf512a..3a549c2 100644 --- a/crate_db/src/builddb.rs +++ b/crate_db/src/builddb.rs @@ -1,8 +1,11 @@ -use rich_crate::Origin; - use parking_lot::Mutex; +use rich_crate::Origin; use rusqlite::*; +use semver::Version as SemVer; use std::path::Path; +use std::collections::HashMap; +use std::collections::BTreeMap; +use std::collections::BTreeSet; pub struct BuildDb { pub(crate) conn: Mutex, @@ -10,11 +13,24 @@ pub struct BuildDb { #[derive(Debug, Clone)] pub struct CompatibilityInfo { - pub rustc_version: String, - pub crate_version: String, + pub rustc_version: SemVer, + pub crate_version: SemVer, pub compat: Compat, } +#[derive(Debug)] +pub struct CompatRange { + pub oldest_ok: SemVer, + pub newest_bad: SemVer, + pub newest_ok: SemVer, +} + +#[derive(Debug)] +pub struct RustcCompatRange { + pub newest_ok: Option, + pub oldest_bad: Option, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum Compat { VerifiedWorks, @@ -23,6 +39,27 @@ pub enum Compat { Incompatible, } +impl Compat { + pub fn from_str(s: &str) -> Self { + match s { + "Y" => Compat::VerifiedWorks, + "y" => Compat::ProbablyWorks, + "n" => Compat::BrokenDeps, + "N" => Compat::Incompatible, + _ => panic!("bad compat str {}", s), + } + } + + pub fn as_str(self) -> &'static str { + match self { + Compat::VerifiedWorks => "Y", + Compat::ProbablyWorks => "y", + Compat::BrokenDeps => "n", + Compat::Incompatible => "N", + } + } +} + impl BuildDb { pub fn new(path: impl AsRef) -> Result { let db = Connection::open(path.as_ref())?; @@ -51,33 +88,76 @@ impl BuildDb { let conn = self.conn.lock(); let mut get = conn.prepare_cached(r"SELECT rustc_version, version, compat FROM build_results WHERE origin = ?1")?; let origin_str = origin.to_str(); - let res = get.query_map(&[origin_str.as_str()], |row| { - let compat = match row.get_raw(2).as_str().expect("strtype") { - "Y" => Compat::VerifiedWorks, - "y" => Compat::ProbablyWorks, - "n" => Compat::BrokenDeps, - "N" => Compat::Incompatible, - _ => panic!("wat?"), - }; - Ok(CompatibilityInfo { - rustc_version: row.get(0)?, - crate_version: row.get(1)?, - compat - }) - })?; + let res = get.query_map(&[origin_str.as_str()], Self::compat_row)?; res.collect() } + pub fn get_all_compat(&self) -> Result)>> { + let conn = self.conn.lock(); + let mut get = conn.prepare_cached(r"SELECT rustc_version, version, compat, origin FROM build_results")?; + + let mut by_crate = HashMap::with_capacity(10000); + + let max_ver: SemVer = "999.999.999".parse().unwrap(); + let min_ver: SemVer = "0.0.0".parse().unwrap(); + + for row in get.query_map(NO_PARAMS, |row| Ok((Origin::from_str(row.get_raw(3).as_str().unwrap()), Self::compat_row(row)?)))? { + let (origin, compat) = row?; + let by_ver = by_crate.entry(origin).or_insert_with(BTreeMap::default); + let t = by_ver.entry(compat.crate_version).or_insert_with(|| CompatRange { + oldest_ok: max_ver.clone(), + newest_ok: min_ver.clone(), + newest_bad: min_ver.clone(), + }); + match compat.compat { + Compat::VerifiedWorks | Compat::ProbablyWorks => { + if compat.rustc_version < t.oldest_ok { + t.oldest_ok = compat.rustc_version.clone(); + } + if compat.rustc_version > t.newest_ok { + t.newest_ok = compat.rustc_version; + } + }, + Compat::Incompatible | Compat::BrokenDeps => { + if compat.rustc_version > t.newest_bad { + t.newest_bad = compat.rustc_version; + } + }, + } + } + Ok(by_crate.into_iter().map(|(origin, compat)| { + let mut rustc_versions = BTreeSet::new(); + for (_, c) in &compat { + if c.oldest_ok != max_ver {rustc_versions.insert(&c.oldest_ok);} + if c.newest_bad != min_ver {rustc_versions.insert(&c.newest_bad);} + if c.newest_ok != min_ver {rustc_versions.insert(&c.newest_ok);} + } + let rver = rustc_versions.into_iter().map(|rv| { + let oldest_bad = compat.iter().filter(|(_, rustc)| rv <= &rustc.newest_bad).map(|(crate_ver, _)| crate_ver).min(); + let newest_ok = compat.iter().filter(|(_, rustc)| rv >= &rustc.oldest_ok).map(|(crate_ver, _)| crate_ver).max(); + (rv.clone(), RustcCompatRange { + oldest_bad: oldest_bad.cloned(), + newest_ok: newest_ok.cloned(), + }) + }).collect(); + (origin, rver) + }).collect()) + } + + fn compat_row(row: &Row) -> Result { + let compat = Compat::from_str(row.get_raw(2).as_str().expect("strtype")); + Ok(CompatibilityInfo { + rustc_version: SemVer::parse(row.get_raw(0).as_str().unwrap()).expect("semver"), + crate_version: SemVer::parse(row.get_raw(1).as_str().unwrap()).expect("semver"), + compat + }) + } + pub fn set_compat(&self, origin: &Origin, ver: &str, rustc_version: &str, compat: Compat) -> Result<()> { let conn = self.conn.lock(); let mut ins = conn.prepare_cached(r"INSERT OR REPLACE INTO build_results(origin, version, rustc_version, compat) VALUES(?1, ?2, ?3, ?4)")?; let origin_str = origin.to_str(); - let result_str = match compat { - Compat::VerifiedWorks => "Y", - Compat::ProbablyWorks => "y", - Compat::BrokenDeps => "n", - Compat::Incompatible => "N", - }; + let result_str = compat.as_str(); ins.execute(&[origin_str.as_str(), ver, rustc_version, result_str])?; Ok(()) } -- cgit v1.2.3