summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-04-24 10:13:39 +0200
committerGitHub <noreply@github.com>2019-04-24 10:13:39 +0200
commitc83ef519fac21f0943252e5e303dd733edd2b870 (patch)
treeda74650c8597fed1f194a123aa8d610943428f71
parent4f3aebf1d71e98a8c78495838e229ea3999a8db1 (diff)
parent5f34e97efb8d67d7b3763d53d747d06661bde558 (diff)
Merge pull request #6 from matthiasbeyer/feature-compare
Compare feature
-rw-r--r--Cargo.toml8
-rw-r--r--librepology/src/v1/types/category.rs2
-rw-r--r--librepology/src/v1/types/download.rs2
-rw-r--r--librepology/src/v1/types/effname.rs2
-rw-r--r--librepology/src/v1/types/license.rs2
-rw-r--r--librepology/src/v1/types/maintainer.rs2
-rw-r--r--librepology/src/v1/types/name.rs2
-rw-r--r--librepology/src/v1/types/repo.rs2
-rw-r--r--librepology/src/v1/types/summary.rs2
-rw-r--r--librepology/src/v1/types/version.rs2
-rw-r--r--librepology/src/v1/types/www.rs2
-rw-r--r--src/cli.rs31
-rw-r--r--src/compare.rs15
-rw-r--r--src/frontend/json.rs41
-rw-r--r--src/frontend/list.rs27
-rw-r--r--src/frontend/mod.rs3
-rw-r--r--src/frontend/table.rs40
-rw-r--r--src/main.rs46
18 files changed, 220 insertions, 11 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 877701b..290c61a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,3 +26,11 @@ filters = "0.3"
version = ">=2.33"
default-features = false
features = [ "suggestions", "color", "wrap_help" ]
+
+[dependencies.csv]
+version = "1"
+optional = true
+
+[features]
+default = ["compare_csv"]
+compare_csv = ["csv"] \ No newline at end of file
diff --git a/librepology/src/v1/types/category.rs b/librepology/src/v1/types/category.rs
index 1bc8c02..975fe7f 100644
--- a/librepology/src/v1/types/category.rs
+++ b/librepology/src/v1/types/category.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// list of package categories
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Category(String);
impl Deref for Category {
diff --git a/librepology/src/v1/types/download.rs b/librepology/src/v1/types/download.rs
index e337baa..d52a2db 100644
--- a/librepology/src/v1/types/download.rs
+++ b/librepology/src/v1/types/download.rs
@@ -3,7 +3,7 @@ use std::ops::Deref;
use url::Url;
// list of package downloads
-#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, new)]
pub struct Download(#[serde(with = "url_serde")] Url);
impl Deref for Download {
diff --git a/librepology/src/v1/types/effname.rs b/librepology/src/v1/types/effname.rs
index ae38fd7..118f56e 100644
--- a/librepology/src/v1/types/effname.rs
+++ b/librepology/src/v1/types/effname.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// list of package downloads
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct EffName(String);
impl Deref for EffName {
diff --git a/librepology/src/v1/types/license.rs b/librepology/src/v1/types/license.rs
index 7d67a2e..f755888 100644
--- a/librepology/src/v1/types/license.rs
+++ b/librepology/src/v1/types/license.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// list of package licenses
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct License(String);
impl Deref for License {
diff --git a/librepology/src/v1/types/maintainer.rs b/librepology/src/v1/types/maintainer.rs
index d76c879..f97fc91 100644
--- a/librepology/src/v1/types/maintainer.rs
+++ b/librepology/src/v1/types/maintainer.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// list of package maintainers
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Maintainer(String);
impl Deref for Maintainer {
diff --git a/librepology/src/v1/types/name.rs b/librepology/src/v1/types/name.rs
index 649e70d..70e3317 100644
--- a/librepology/src/v1/types/name.rs
+++ b/librepology/src/v1/types/name.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// package name as in repository (if different from version)
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Name(String);
impl Deref for Name {
diff --git a/librepology/src/v1/types/repo.rs b/librepology/src/v1/types/repo.rs
index 7305ec6..062358e 100644
--- a/librepology/src/v1/types/repo.rs
+++ b/librepology/src/v1/types/repo.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// name of repository for this package
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Repo(String);
impl Deref for Repo {
diff --git a/librepology/src/v1/types/summary.rs b/librepology/src/v1/types/summary.rs
index 494e671..0595fef 100644
--- a/librepology/src/v1/types/summary.rs
+++ b/librepology/src/v1/types/summary.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// one-line description of the package
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Summary(String);
impl Deref for Summary {
diff --git a/librepology/src/v1/types/version.rs b/librepology/src/v1/types/version.rs
index cad703a..7ae7b85 100644
--- a/librepology/src/v1/types/version.rs
+++ b/librepology/src/v1/types/version.rs
@@ -1,7 +1,7 @@
use std::ops::Deref;
// package version (sanitized)
-#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug, Serialize, Deserialize, new)]
pub struct Version(String);
impl Deref for Version {
diff --git a/librepology/src/v1/types/www.rs b/librepology/src/v1/types/www.rs
index a59ef02..1b5b89e 100644
--- a/librepology/src/v1/types/www.rs
+++ b/librepology/src/v1/types/www.rs
@@ -2,7 +2,7 @@ use std::ops::Deref;
use url::Url;
// list of package webpages
-#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
+#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, new)]
pub struct Www(#[serde(with = "url_serde")] Url);
impl Deref for Www {
diff --git a/src/cli.rs b/src/cli.rs
index 25533cc..cf06dbe 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -90,6 +90,37 @@ pub fn build_cli<'a>() -> App<'a, 'a> {
.required(true))
)
+
+ .subcommand(SubCommand::with_name("compare")
+ .about("Compare a list of packages to distro repositories")
+ .arg(Arg::with_name("compare-list")
+ .index(1)
+ .required(true)
+ .multiple(false)
+ .takes_value(true)
+ .value_name("FILE")
+ .help("Compare the data from this list to a list of distros out there. Supports JSON and CSV, based on file extension (.json / .csv)"))
+ .arg(Arg::with_name("compare-distros")
+ .index(2)
+ .required(true)
+ .multiple(true)
+ .takes_value(true)
+ .value_name("DIST")
+ .help("A list of repology distribution names to compare to"))
+
+ .after_help(r#"
+ Compare a list of packages to all supplied repology distributions.
+ The list of packages shall have the following format:
+
+ * CSV:
+ Header: name;version;comment
+
+ * JSON:
+ { "name": "...", "version": "...", "comment": "..." }
+
+ "#)
+ )
+
.after_help(r#"
repolocli can read data from stdin, if you want to postprocess repology.org data you already
fetched from repology.org/api/v1 via curl (or some other method).
diff --git a/src/compare.rs b/src/compare.rs
new file mode 100644
index 0000000..c13d01e
--- /dev/null
+++ b/src/compare.rs
@@ -0,0 +1,15 @@
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
+pub struct ComparePackage {
+ name: String,
+ version: String,
+}
+
+impl ComparePackage {
+ pub fn name(&self) -> &String {
+ &self.name
+ }
+
+ pub fn version(&self) -> &String {
+ &self.version
+ }
+} \ No newline at end of file
diff --git a/src/frontend/json.rs b/src/frontend/json.rs
index 9cf5573..b6a34e5 100644
--- a/src/frontend/json.rs
+++ b/src/frontend/json.rs
@@ -4,10 +4,14 @@ use std::ops::Deref;
use librepology::v1::types::Package;
use librepology::v1::types::Problem;
+use librepology::v1::types::Repo;
use failure::Fallible as Result;
use failure::Error;
use crate::frontend::Frontend;
+use crate::backend::Backend;
+use crate::compare::ComparePackage;
+use librepology::v1::api::Api;
pub struct JsonFrontend(Stdout);
@@ -29,5 +33,42 @@ impl Frontend for JsonFrontend {
let mut outlock = self.0.lock();
writeln!(outlock, "{}", output).map_err(Error::from)
}
+
+ fn compare_packages(&self, packages: Vec<ComparePackage>, backend: &Backend, filter_repos: Vec<Repo>) -> Result<()> {
+ #[derive(Serialize)]
+ struct PackageComp {
+ // not optimal, as we have to clone the inner variables from the package
+ // but using references is too complicated right now
+ package_name: String,
+ local_version: String,
+ upstream_repo: String,
+ upstream_version: String,
+ }
+
+ let mut output: Vec<PackageComp> = vec![];
+
+ for package in packages.iter() {
+ let mut list = backend
+ .project(package.name().deref())?
+ .into_iter()
+ .filter(|p| filter_repos.contains(p.repo()))
+ .map(|upstream_package| {
+ PackageComp {
+ package_name: package.name().clone(),
+ local_version: package.version().clone(),
+ upstream_repo: upstream_package.repo().deref().clone(),
+ upstream_version: upstream_package.version().deref().clone(),
+ }
+ })
+ .collect::<Vec<_>>();
+
+ output.append(&mut list);
+ }
+
+ let output = serde_json::ser::to_string_pretty(&output)?;
+
+ let mut outlock = self.0.lock();
+ writeln!(outlock, "{}", output).map_err(Error::from)
+ }
}
diff --git a/src/frontend/list.rs b/src/frontend/list.rs
index 8cff146..e870ab0 100644
--- a/src/frontend/list.rs
+++ b/src/frontend/list.rs
@@ -4,10 +4,14 @@ use std::ops::Deref;
use librepology::v1::types::Package;
use librepology::v1::types::Problem;
+use librepology::v1::types::Repo;
use failure::Fallible as Result;
use failure::Error;
use crate::frontend::Frontend;
+use crate::backend::Backend;
+use crate::compare::ComparePackage;
+use librepology::v1::api::Api;
pub struct ListFrontend(Stdout);
@@ -67,5 +71,28 @@ impl Frontend for ListFrontend {
})
})
}
+
+ fn compare_packages(&self, packages: Vec<ComparePackage>, backend: &Backend, filter_repos: Vec<Repo>) -> Result<()> {
+ let mut output = self.0.lock();
+
+ for package in packages {
+ backend
+ .project(package.name().deref())?
+ .into_iter()
+ .filter(|p| filter_repos.contains(p.repo()))
+ .map(|upstream_package| {
+ writeln!(output,
+ "{our_package_name} - {our_package_version} - {up_repo_name} - {up_package_version}",
+ our_package_name = package.name().deref(),
+ our_package_version = package.version().deref(),
+ up_repo_name = upstream_package.repo().deref(),
+ up_package_version = upstream_package.version().deref()
+ ).map_err(Error::from)
+ })
+ .collect::<Result<Vec<()>>>()?;
+ }
+
+ Ok(())
+ }
}
diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs
index 41ab972..43e5b5e 100644
--- a/src/frontend/mod.rs
+++ b/src/frontend/mod.rs
@@ -7,10 +7,13 @@ use crate::config::Configuration;
use crate::frontend::list::ListFrontend;
use crate::frontend::json::JsonFrontend;
use crate::frontend::table::TableFrontend;
+use crate::compare::ComparePackage;
+use crate::backend::Backend;
pub trait Frontend {
fn list_packages(&self, packages: Vec<Package>) -> Result<()>;
fn list_problems(&self, problems: Vec<Problem>) -> Result<()>;
+ fn compare_packages(&self, packages: Vec<ComparePackage>, backend: &Backend, filter_repos: Vec<Repo>) -> Result<()>;
}
pub mod list;
diff --git a/src/frontend/table.rs b/src/frontend/table.rs
index ba4e77a..6a30e9c 100644
--- a/src/frontend/table.rs
+++ b/src/frontend/table.rs
@@ -3,11 +3,15 @@ use std::ops::Deref;
use librepology::v1::types::Package;
use librepology::v1::types::Problem;
+use librepology::v1::types::Repo;
use failure::Fallible as Result;
use prettytable::format;
use prettytable::Table;
use crate::frontend::Frontend;
+use crate::backend::Backend;
+use crate::compare::ComparePackage;
+use librepology::v1::api::Api;
pub struct TableFrontend(Stdout);
@@ -90,5 +94,41 @@ impl Frontend for TableFrontend {
Ok(())
}
+
+ fn compare_packages(&self, packages: Vec<ComparePackage>, backend: &Backend, filter_repos: Vec<Repo>) -> Result<()> {
+ let mut table = Table::new();
+ let format = format::FormatBuilder::new()
+ .column_separator('|')
+ .borders('|')
+ .separators(
+ &[format::LinePosition::Title, format::LinePosition::Top, format::LinePosition::Bottom],
+ format::LineSeparator::new('-', '+', '+', '+')
+ )
+ .padding(1, 1)
+ .build();
+ table.set_format(format);
+
+ table.set_titles(row!["Name", "Local Version", "Repo", "Upstream Version"]);
+
+ for package in packages {
+ backend
+ .project(package.name().deref())?
+ .into_iter()
+ .filter(|p| filter_repos.contains(p.repo()))
+ .for_each(|upstream_package| {
+ table.add_row(row![
+ package.name().deref().clone(),
+ package.version().deref().clone(),
+ upstream_package.repo().deref().clone(),
+ upstream_package.version().deref().clone(),
+ ]);
+ });
+ }
+
+ let mut outlock = self.0.lock();
+ table.print(&mut outlock)?;
+
+ Ok(())
+ }
}
diff --git a/src/main.rs b/src/main.rs
index dddbe42..9acc496 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,9 @@ extern crate reqwest;
extern crate tokio;
extern crate filters;
+#[cfg(feature = "compare_csv")]
+extern crate csv;
+
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate log;
#[macro_use] extern crate failure;
@@ -18,8 +21,13 @@ mod config;
mod backend;
mod frontend;
mod cli;
+mod compare;
use std::path::PathBuf;
+
+#[cfg(feature = "compare_csv")]
+use std::io::Cursor;
+
use failure::err_msg;
use failure::Error;
use failure::Fallible as Result;
@@ -27,6 +35,7 @@ use clap::ArgMatches;
use filters::filter::Filter;
use config::Configuration;
+use compare::ComparePackage;
use librepology::v1::api::Api;
use librepology::v1::types::Repo;
@@ -57,6 +66,33 @@ fn initialize_logging(app: &ArgMatches) -> Result<()> {
.map_err(Error::from)
}
+fn deserialize_package_list(s: String, filepath: &str) -> Result<Vec<ComparePackage>> {
+ let pb = PathBuf::from(filepath);
+ let ext = pb
+ .extension()
+ .ok_or_else(|| format_err!("Couldn't get file extension: {}", filepath))?
+ .to_str()
+ .ok_or_else(|| format_err!("Not valid Unicode: {}", filepath))?;
+
+ match ext {
+ "json" => {
+ serde_json::from_str(&s).map_err(Error::from)
+ },
+
+ #[cfg(feature = "compare_csv")]
+ "csv" => {
+ let cursor = Cursor::new(s);
+ let mut v : Vec<ComparePackage> = vec![];
+ for element in csv::Reader::from_reader(cursor).deserialize() {
+ v.push(element?);
+ }
+ Ok(v)
+ },
+
+ other => Err(format_err!("Unknown file extension: {}", other))?,
+ }
+}
+
fn main() -> Result<()> {
let app = cli::build_cli().get_matches();
initialize_logging(&app)?;
@@ -145,7 +181,15 @@ fn main() -> Result<()> {
.collect();
frontend.list_problems(problems)?;
- }
+ },
+ ("compare", Some(mtch)) => {
+ let repos = mtch.values_of("compare-distros").unwrap().map(|s| Repo::new(String::from(s))).collect();
+ let file_path = mtch.value_of("compare-list").unwrap(); // safe by clap
+ let content = ::std::fs::read_to_string(file_path).map_err(Error::from)?;
+ let pkgs : Vec<ComparePackage> = deserialize_package_list(content, file_path)?;
+
+ frontend.compare_packages(pkgs, &backend, repos)?;
+ },
(other, _mtch) => {
if app.is_present("input_stdin") {