summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2021-04-06 12:04:17 +0200
committerMatthias Beyer <mail@beyermatthias.de>2021-04-06 12:04:17 +0200
commitc8580bc601330d3d00bfe492a00464f5c403ccb5 (patch)
tree788b10e8473a70c9f1aab8a4022ccdd440802386
parent32b24f4e03d0dc48db7f7d9927501b07b4821c33 (diff)
Remove CLI
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--src/backend.rs56
-rw-r--r--src/cli.rs150
-rw-r--r--src/config.rs28
-rw-r--r--src/filter.rs49
-rw-r--r--src/frontend/json.rs41
-rw-r--r--src/frontend/list.rs85
-rw-r--r--src/frontend/mod.rs41
-rw-r--r--src/frontend/table.rs94
-rw-r--r--src/main.rs261
9 files changed, 1 insertions, 804 deletions
diff --git a/src/backend.rs b/src/backend.rs
deleted file mode 100644
index e260e8c..0000000
--- a/src/backend.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use clap::ArgMatches;
-
-use librepology::v1::api::Api;
-use librepology::v1::buffer::BufferApi;
-use librepology::v1::error::Result;
-use librepology::v1::restapi::RestApi;
-use librepology::v1::types::*;
-
-use crate::config::Configuration;
-
-/// Helper type for cli implementation
-/// for being transparent in what backend we use
-pub enum Backend {
- Buffer(BufferApi),
- RepologyOrg(RestApi),
-}
-
-/// Implement Api for Backend
-///
-/// With this, we can use the `Backend` object and do not have to care whether we have a librepology::
-impl Api for Backend {
- fn project<N: AsRef<str>>(&self, name: N) -> Result<Vec<Package>> {
- match self {
- Backend::Buffer(inner) => inner.project(name),
- Backend::RepologyOrg(inner) => inner.project(name),
- }
- }
-
- fn problems_for_repo<R: AsRef<str>>(&self, repo: R) -> Result<Vec<Problem>> {
- match self {
- Backend::Buffer(inner) => inner.problems_for_repo(repo),
- Backend::RepologyOrg(inner) => inner.problems_for_repo(repo),
- }
- }
-
- fn problems_for_maintainer<M: AsRef<str>>(&self, maintainer: M) -> Result<Vec<Problem>> {
- match self {
- Backend::Buffer(inner) => inner.problems_for_maintainer(maintainer),
- Backend::RepologyOrg(inner) => inner.problems_for_maintainer(maintainer),
- }
- }
-}
-
-pub fn new_backend(app: &ArgMatches, config: &Configuration) -> anyhow::Result<Backend> {
- if app.is_present("input_stdin") {
- trace!("Building new STDIN backend");
- BufferApi::read_from(std::io::stdin())
- .map(Backend::Buffer)
- .map_err(anyhow::Error::from)
- } else {
- trace!("Building new remote backend");
- let url = config.repology_url().as_str().into();
- trace!("url = {}", url);
- Ok(Backend::RepologyOrg(RestApi::new(url)))
- }
-}
diff --git a/src/cli.rs b/src/cli.rs
deleted file mode 100644
index 871f941..0000000
--- a/src/cli.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use clap::{App, Arg, ArgGroup, SubCommand};
-
-pub fn build_cli<'a>() -> App<'a, 'a> {
- App::new("repolocli")
- .version("0.1")
- .author("Matthias Beyer <mail@beyermatthias.de>")
- .about("Query repology.org and postprocess its output")
-
- .arg(Arg::with_name("config")
- .long("config")
- .value_name("PATH")
- .required(false)
- .multiple(false)
- .takes_value(true)
- .help("Override default configuration file path")
-
- )
-
- .arg(Arg::with_name("verbose")
- .long("verbose")
- .short("v")
- .required(false)
- .multiple(true)
- .takes_value(false)
- .help("Increase verbosity. Default = Info, -v = Debug, -vv = Trace")
- )
-
- .arg(Arg::with_name("quiet")
- .long("quiet")
- .short("q")
- .required(false)
- .multiple(true)
- .takes_value(false)
- .help("Decrease verbosity. Default = Info, -q = Warn, -qq = Error")
- )
-
- .arg(Arg::with_name("output")
- .long("output")
- .short("o")
- .required(false)
- .multiple(false)
- .takes_value(true)
- .possible_values(&["table", "json", "lines"])
- .default_value("lines")
- .help("Output format")
- )
-
- .arg(Arg::with_name("input_stdin")
- .long("stdin")
- .short("I")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Read data (JSON) from stdin.")
- )
-
- .subcommand(SubCommand::with_name("project")
- .arg(Arg::with_name("project_name")
- .index(1)
- .required(false) // TODO: Make required, is not required currently when --stdin is passed.
- .multiple(false)
- .takes_value(true)
- .help("Query data about a project")
- )
-
- .arg(Arg::with_name("sort-version")
- .long("sort-version")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Sort output by version")
- .conflicts_with("sort-repo")
- )
- .arg(Arg::with_name("sort-repo")
- .long("sort-repo")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Sort output by repository")
- .conflicts_with("sort-version")
- )
- .arg(Arg::with_name("latest")
- .long("latest")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Try to find the lastest version (version is string-compared if not used with --semver)")
- .conflicts_with("sort-version")
- .conflicts_with("sort-repo")
- )
- .arg(Arg::with_name("semver")
- .long("semver")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .requires("latest")
- .help("Try to find latest version using semver. If semver could not be parsed, equality is assumed, which might yield bogus results.")
- )
- )
-
- .subcommand(SubCommand::with_name("problems")
- .arg(Arg::with_name("repo")
- .short("r")
- .long("repo")
- .alias("repository")
- .required(false)
- .multiple(false)
- .takes_value(true)
- .help("The repository to get problems for")
- )
-
- .arg(Arg::with_name("maintainer")
- .short("m")
- .long("maintainer")
- .alias("maint")
- .required(false)
- .multiple(false)
- .takes_value(true)
- .help("The maintainer to get problems for")
- )
-
- .group(ArgGroup::with_name("problems-args")
- .args(&["repo", "maintainer"])
- .required(true))
-
-
- .arg(Arg::with_name("sort-maintainer")
- .long("sort-maintainer")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Sort output by maintainer")
- .conflicts_with("sort-repo")
- )
- .arg(Arg::with_name("sort-repo")
- .long("sort-repo")
- .required(false)
- .multiple(false)
- .takes_value(false)
- .help("Sort output by repository")
- .conflicts_with("sort-maintainer")
- )
- )
-
- .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).
- In this case, repolocli is only a easier-to-use 'jq' (if you don't know jq, look it up NOW!).
- "#)
-}
diff --git a/src/config.rs b/src/config.rs
deleted file mode 100644
index 240b13f..0000000
--- a/src/config.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use url::Url;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Configuration {
- #[serde(with = "url_serde")]
- #[serde(rename = "repology_url")]
- repology_url: Url,
-
- #[serde(rename = "whitelist")]
- whitelist: Vec<String>,
-
- #[serde(rename = "blacklist")]
- blacklist: Vec<String>,
-}
-
-impl Configuration {
- pub fn repology_url(&self) -> &Url {
- &self.repology_url
- }
-
- pub fn whitelist(&self) -> &Vec<String> {
- &self.whitelist
- }
-
- pub fn blacklist(&self) -> &Vec<String> {
- &self.blacklist
- }
-}
diff --git a/src/filter.rs b/src/filter.rs
deleted file mode 100644
index 334ccbd..0000000
--- a/src/filter.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use filters::filter::Filter;
-use filters::ops::and::And;
-use filters::ops::bool::Bool;
-use filters::ops::not::Not;
-
-use crate::config::Configuration;
-
-struct BlackListFilter {
- repo_name: String,
-}
-
-impl BlackListFilter {
- pub fn new(repo_name: String) -> Self {
- BlackListFilter { repo_name }
- }
-}
-
-impl Filter<String> for BlackListFilter {
- fn filter(&self, element: &String) -> bool {
- element != self.repo_name
- }
-}
-
-struct WhiteListFilter {
- repo_name: String,
-}
-
-impl Filter<String> for WhiteListFilter {
- fn filter(&self, element: &String) -> bool {
- element == self.repo_name
- }
-}
-
-pub fn repo_filter(config: &Configuration) -> Box<Filter<String>> {
- let blacklist = config
- .blacklist()
- .iter()
- .cloned()
- .map(BlackListFilter::new)
- .fold(Box::new(Bool::new(true)), |accu, element| accu.and(element));
- let whitelist = config
- .whitelist()
- .iter()
- .cloned()
- .map(WhiteListFilter::new)
- .fold(Box::new(Bool::new(true)), |accu, element| accu.and(element));
-
- Box::new(blacklist.not().or(whitelist))
-}
diff --git a/src/frontend/json.rs b/src/frontend/json.rs
deleted file mode 100644
index 2a7f40a..0000000
--- a/src/frontend/json.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use std::io::Stdout;
-use std::io::Write;
-
-use anyhow::Error;
-use anyhow::Result;
-use librepology::v1::types::Package;
-use librepology::v1::types::Problem;
-
-use crate::frontend::Frontend;
-
-pub struct JsonFrontend(Stdout);
-
-/// A Frontend that serializes the data to JSON
-///
-/// Useful for piping the data as structured data to another program.
-///
-/// # Warning
-///
-/// This frontend does _not_ maintain compatibility with repolocli itself. That means that piping
-/// output from repolocli to repolocli is _NOT_ supported by this frontend.
-///
-impl JsonFrontend {
- pub fn new(stdout: Stdout) -> Self {
- JsonFrontend(stdout)
- }
-
- fn write(&self, output: String) -> Result<()> {
- let mut outlock = self.0.lock();
- writeln!(outlock, "{}", output).map_err(Error::from)
- }
-}
-
-impl Frontend for JsonFrontend {
- fn list_packages(&self, packages: Vec<Package>) -> Result<()> {
- self.write(serde_json::ser::to_string_pretty(&packages).map_err(Error::from)?)
- }
-
- fn list_problems(&self, problems: Vec<Problem>) -> Result<()> {
- self.write(serde_json::ser::to_string_pretty(&problems).map_err(Error::from)?)
- }
-}
diff --git a/src/frontend/list.rs b/src/frontend/list.rs
deleted file mode 100644
index 9e06828..0000000
--- a/src/frontend/list.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use std::io::Stdout;
-use std::io::Write;
-use std::ops::Deref;
-
-use anyhow::Error;
-use anyhow::Result;
-use librepology::v1::types::Name;
-use librepology::v1::types::Package;
-use librepology::v1::types::Problem;
-
-use crate::frontend::Frontend;
-
-pub struct ListFrontend(Stdout);
-
-/// A Frontend that prints the data in a human-readable way but without ASCII-art.
-///
-/// It seperates the values with dashes ("-") for a slightly better reading experience.
-impl ListFrontend {
- pub fn new(stdout: Stdout) -> Self {
- ListFrontend(stdout)
- }
-}
-
-impl Frontend for ListFrontend {
- fn list_packages(&self, packages: Vec<Package>) -> Result<()> {
- let mut outlock = self.0.lock();
-
- packages.iter().fold(Ok(()), |accu, package| {
- accu.and_then(|_| {
- let status = if let Some(stat) = package.status() {
- stat.deref().to_string()
- } else {
- String::from("No status")
- }; // not optimal, but works for now.
-
- let url = if let Some(url) = package.www() {
- if let Some(url) = url.first() {
- url.deref().to_string()
- } else {
- String::from("")
- }
- } else {
- String::from("")
- }; // not optimal, but works for now
-
- writeln!(
- outlock,
- "{name:10} - {version:8} - {repo:15} - {status:5} - {www}",
- name = package
- .any_name()
- .map(Name::deref)
- .map(String::deref)
- .unwrap_or_else(|| "<unknown>"),
- version = package.version().deref(),
- repo = package.repo().deref(),
- status = status,
- www = url
- )
- .map(|_| ())
- .map_err(Error::from)
- })
- })
- }
-
- fn list_problems(&self, problems: Vec<Problem>) -> Result<()> {
- let mut outlock = self.0.lock();
-
- problems.iter().fold(Ok(()), |accu, problem| {
- accu.and_then(|_| {
- writeln!(
- outlock,
- "{repo:10} - {name:10} - {effname:10} - {maintainer:15} - {desc}",
- repo = problem.repo().deref(),
- name = problem.name().deref(),
- effname = problem.effname().deref(),
- maintainer = problem.maintainer().deref(),
- desc = problem.problem_description()
- )
- .map(|_| ())
- .map_err(Error::from)
- })
- })
- }
-
-}
diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs
deleted file mode 100644
index f1d5001..0000000
--- a/src/frontend/mod.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use anyhow::Result;
-use clap::ArgMatches;
-
-use librepology::v1::types::*;
-
-use crate::config::Configuration;
-use crate::frontend::json::JsonFrontend;
-use crate::frontend::list::ListFrontend;
-use crate::frontend::table::TableFrontend;
-
-/// A Frontend represents a way to show the data to the user
-pub trait Frontend {
- fn list_packages(&self, packages: Vec<Package>) -> Result<()>;
- fn list_problems(&self, problems: Vec<Problem>) -> Result<()>;
-}
-
-pub mod json;
-pub mod list;
-pub mod table;
-
-/// Helper function for building a new Frontend object based on the commandline parameters
-pub fn new_frontend(app: &ArgMatches, _config: &Configuration) -> Result<Box<dyn Frontend>> {
- match app.value_of("output") {
- None | Some("lines") => {
- debug!("No output specified, using default");
- Ok(Box::new(ListFrontend::new(::std::io::stdout())))
- }
-
- Some("json") => {
- debug!("Using JSON Frontend");
- Ok(Box::new(JsonFrontend::new(::std::io::stdout())))
- }
-
- Some("table") => {
- debug!("Using table Frontend");
- Ok(Box::new(TableFrontend::new(::std::io::stdout())))
- }
-
- Some(other) => Err(format_err!("Unknown Frontend '{}'", other)),
- }
-}
diff --git a/src/frontend/table.rs b/src/frontend/table.rs
deleted file mode 100644
index 6a18af3..0000000
--- a/src/frontend/table.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-use std::io::Stdout;
-use std::ops::Deref;
-
-use anyhow::Result;
-use librepology::v1::types::Name;
-use librepology::v1::types::Package;
-use librepology::v1::types::Problem;
-use prettytable::format;
-use prettytable::Table;
-
-use crate::frontend::Frontend;
-
-/// A Frontend that formats the output in a nice ASCII-art table
-pub struct TableFrontend(Stdout);
-
-impl TableFrontend {
- pub fn new(stdout: Stdout) -> Self {
- TableFrontend(stdout)
- }
-
- fn mktable(&self) -> Table {
- 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", "Version", "Repo", "Status", "URL"]);
- table
- }
-
- fn print(&self, table: Table) -> Result<()> {
- let mut outlock = self.0.lock();
- table.print(&mut outlock)?;
- Ok(())
- }
-}
-
-impl Frontend for TableFrontend {
- fn list_packages(&self, packages: Vec<Package>) -> Result<()> {
- let mut table = self.mktable();
- packages.iter().for_each(|package| {
- let status = if let Some(stat) = package.status() {
- format!("{}", stat)
- } else {
- String::from("No status")
- }; // not optimal, but works for now.
-
- let url = if let Some(url) = package.www() {
- if let Some(url) = url.first() {
- format!("{}", url.deref())
- } else {
- String::from("")
- }
- } else {
- String::from("")
- }; // not optimal, but works for now
-
- let name = package
- .any_name()
- .map(Name::deref)
- .map(String::clone)
- .unwrap_or_else(|| String::from("<unknown>"));
-
- table.add_row(row![name, package.version(), package.repo(), status, url]);
- });
- self.print(table)
- }
-
- fn list_problems(&self, problems: Vec<Problem>) -> Result<()> {
- let mut table = self.mktable();
- problems.iter().for_each(|problem| {
- trace!("Adding row for: {:?}", problem);
- table.add_row(row![
- problem.repo(),
- problem.name(),
- problem.effname(),
- problem.maintainer(),
- problem.problem_description()
- ]);
- });
- self.print(table)
- }
-
-}
diff --git a/src/main.rs b/src/main.rs
index 838f4a2..f6320bc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,262 +1,3 @@
-extern crate boolinator;
-extern crate filters;
-extern crate flexi_logger;
-extern crate itertools;
-extern crate semver;
-extern crate serde;
-extern crate serde_json;
-extern crate toml;
-extern crate url;
-extern crate xdg;
-
-#[macro_use]
-extern crate serde_derive;
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate anyhow;
-#[macro_use]
-extern crate prettytable;
-
-mod backend;
-mod cli;
-mod config;
-mod frontend;
-
-use std::cmp::Ordering;
-use std::path::PathBuf;
-
-use anyhow::Context;
-use anyhow::Error;
-use anyhow::Result;
-use boolinator::Boolinator;
-use clap::ArgMatches;
-use filters::filter::Filter;
-use itertools::Itertools;
-use semver::Version as SemverVersion;
-
-use config::Configuration;
-use librepology::v1::api::Api;
-use librepology::v1::types::Package;
-use librepology::v1::types::Repo;
-
-fn initialize_logging(app: &ArgMatches) -> Result<()> {
- let verbosity = app.occurrences_of("verbose");
- let quietness = app.occurrences_of("quiet");
- let sum = verbosity as i64 - quietness as i64;
- let mut level_filter = flexi_logger::LevelFilter::Info;
-
- if sum == 1 {
- level_filter = flexi_logger::LevelFilter::Debug;
- } else if sum >= 2 {
- level_filter = flexi_logger::LevelFilter::Trace;
- } else if sum == -1 {
- level_filter = flexi_logger::LevelFilter::Warn;
- } else if sum <= -2 {
- level_filter = flexi_logger::LevelFilter::Error;
- }
-
- let mut builder = flexi_logger::LogSpecBuilder::new();
- builder.default(level_filter);
-
- flexi_logger::Logger::with(builder.build())
- .start()
- .map(|_| {
- debug!("Logger initialized!");
- })
- .map_err(Error::from)
-}
-
-fn app() -> Result<()> {
- let app = cli::build_cli().get_matches();
- initialize_logging(&app)?;
- let config: Configuration = {
- let path = if let Some(path) = app.value_of("config").map(PathBuf::from) {
- debug!("Found passed configuration file at {}", path.display());
- Ok(path)
- } else {
- debug!("Searching for configuration in XDG");
- xdg::BaseDirectories::new()?
- .find_config_file("repolocli.toml")
- .ok_or_else(|| anyhow!("Cannot find repolocli.toml"))
- }?;
-
- debug!("Parsing configuration from file: {}", path.display());
-
- let buffer = std::fs::read_to_string(path).map_err(Error::from)?;
- trace!("Config read into memory");
- toml::de::from_str(&buffer)
- .map_err(Error::from)
- .context("Configuration file parsing")
- }?;
- trace!("Config deserialized");
-
- debug!("Initializing Backend");
- let backend = crate::backend::new_backend(&app, &config)?;
- debug!("Backend initialized");
-
- debug!("Initializing Frontend");
- let frontend = crate::frontend::new_frontend(&app, &config)?;
- debug!("Frontend initialized");
-
- let repository_filter = {
- let blacklist_filter = |repo: &Repo| -> bool {
- if config.blacklist().contains(repo) {
- trace!("In Blacklist: {:?} -> false", repo);
- false
- } else {
- trace!("Not in Blacklist: {:?} -> true", repo);
- true
- }
- };
-
- let whitelist_filter = |repo: &Repo| -> bool {
- if config.whitelist().contains(repo) {
- trace!("In Whitelist: {:?} -> true", repo);
- true
- } else {
- trace!("Not in Whitelist: {:?} -> false", repo);
- false
- }
- };
-
- blacklist_filter.or(whitelist_filter)
- };
- debug!("Repository filter constructed successfully");
-
- match app.subcommand() {
- ("project", Some(mtch)) => {
- debug!("Subcommand: 'project'");
- trace!("sort-versions: {}", mtch.is_present("sort-version"));
- trace!("sort-repository: {}", mtch.is_present("sort-repo"));
-
- let name = if app.is_present("input_stdin") {
- // Ugly, but works:
- // If we have "--stdin" on CLI, we have a CLI/Stdin backend, which means that we can query
- // _any_ "project", and get the stdin anyways. This is really not like it should be, but
- // works for now
- ""
- } else {
- mtch.value_of("project_name").unwrap() // safe by clap
- };
-
- let mut packages: Vec<Package> = {
- debug!("Fetching packages");
- let iter = backend
- .project(&name)?
- .into_iter()
- .filter(|package| repository_filter.filter(package.repo()));
-
- if mtch.is_present("sort-version") {
- trace!("Sorting by version");
- iter.sorted_by(|a, b| Ord::cmp(a.version(), b.version()))
- .collect()
- } else if mtch.is_present("sort-repo") {
- trace!("Sorting by repository");
- iter.sorted_by(|a, b| Ord::cmp(a.repo(), b.repo()))
- .collect()
- } else {
- trace!("Not sorting");
- iter.collect()
- }
- };
-
- let packages = if mtch.is_present("latest") {
- if mtch.is_present("semver") {
- let comp = |a: &Package, b: &Package| {
- let av = SemverVersion::parse(a.version());
- let bv = SemverVersion::parse(b.version());
-
- if let (Ok(av), Ok(bv)) = (av, bv) {
- av.partial_cmp(&bv).unwrap_or(Ordering::Equal)
- } else {
- Ordering::Equal
- }
- };
-
- packages.sort_by(comp);
- } else {
- packages.sort_by(|a, b| {
- a.version()
- .partial_cmp(b.version())
- .unwrap_or(Ordering::Equal)
- });
- }
- packages.pop().into_iter().collect::<Vec<_>>()
- } else {
- packages
- };
-
- debug!("Listing packages in frontend");
- frontend.list_packages(packages)
- }
-
- ("problems", Some(mtch)) => {
- debug!("Subcommand: 'problems'");
-
- let repo = mtch.value_of("repo");
- let maintainer = mtch.value_of("maintainer");
-
- trace!("repo = {:?}", repo);
- trace!("maintainer = {:?}", maintainer);
-
- let problems = {
- debug!("Finding problems...");
- let iter = match (repo, maintainer) {
- (Some(r), None) => backend.problems_for_repo(&r)?,
- (None, Some(m)) => backend.problems_for_maintainer(&m)?,
- (None, None) => unimplemented!(),
- (Some(_), Some(_)) => unimplemented!(),
- }
- .into_iter()
- .filter(|problem| repository_filter.filter(problem.repo()));
-
- if mtch.is_present("sort-maintainer") {
- trace!("Sorting problems by maintainer");
- iter.sorted_by(|a, b| Ord::cmp(a.maintainer(), b.maintainer()))
- .collect()
- } else if mtch.is_present("sort-repo") {
- trace!("Sorting problems by repo");
- iter.sorted_by(|a, b| Ord::cmp(a.repo(), b.repo()))
- .collect()
- } else {
- trace!("Not sorting problems");
- iter.collect()
- }
- };
-
- debug!("Listing problems in frontend");
- frontend.list_problems(problems)
- }
-
- (other, _mtch) => {
- debug!("Subcommand: {}", other);
- app.is_present("input_stdin")
- .as_result((), format_err!("Input not from stdin"))
- .and_then(|_| {
- // Ugly, but works:
- // If we have "--stdin" on CLI, we have a CLI/Stdin backend, which means that we can query
- // _any_ "project", and get the stdin anyways. This is really not like it should be, but
- // works for now
- let packages = backend
- .project("")?
- .into_iter()
- .filter(|package| repository_filter.filter(package.repo()))
- .collect();
-
- debug!("Listing packages");
- frontend.list_packages(packages)
- })
- .map_err(|_| format_err!("Unknown command: {}", other))
- }
- }
-}
-
-fn print_error(e: Error) {
- error!("Error: {}", e);
- e.chain().for_each(|cause| error!("Caused by: {}", cause));
-}
-
fn main() {
- let _ = app().map_err(print_error);
+ println!("Hello World");
}