diff options
-rw-r--r-- | librepology/src/v1/api.rs | 67 | ||||
-rw-r--r-- | librepology/src/v1/mod.rs | 1 | ||||
-rw-r--r-- | librepology/src/v1/restapi.rs | 27 | ||||
-rw-r--r-- | librepology/src/v1/stdinapi.rs | 63 | ||||
-rw-r--r-- | librepology/src/v1/types/mod.rs | 8 | ||||
-rw-r--r-- | src/backend.rs | 5 | ||||
-rw-r--r-- | src/frontend/json.rs | 28 | ||||
-rw-r--r-- | src/frontend/list.rs | 3 | ||||
-rw-r--r-- | src/frontend/mod.rs | 2 | ||||
-rw-r--r-- | src/frontend/table.rs | 66 |
10 files changed, 135 insertions, 135 deletions
diff --git a/librepology/src/v1/api.rs b/librepology/src/v1/api.rs index d740d44..e192022 100644 --- a/librepology/src/v1/api.rs +++ b/librepology/src/v1/api.rs @@ -1,14 +1,16 @@ -use std::io::{Stdin, Read}; -use std::cell::RefCell; -use std::ops::Deref; -use std::ops::DerefMut; - use failure::Fallible as Result; -use failure::Error; use crate::v1::types::Problem; use crate::v1::types::Package; +/// The high-level functionality of the repology API is represented in this trait +/// +/// Each "functionality" is represented via one function. +/// +/// # Note +/// +/// This is implemented as a _trait_ rather than a _struct_ because this way we can reuse the +/// functionality for operating on a stream, for example on stdin as a source of data. pub trait Api { fn project<N: AsRef<str>>(&self, name: N) -> Result<Vec<Package>>; @@ -18,56 +20,3 @@ pub trait Api { } -// -// Api implemented for StdIn (via a Wrapper for interior mutability) -// -// This way we can read the data from stdin and process it -// - -pub struct StdinWrapper(RefCell<Stdin>); - -impl From<Stdin> for StdinWrapper { - fn from(inner: Stdin) -> Self { - StdinWrapper(RefCell::new(inner)) - } -} - -impl Deref for StdinWrapper { - type Target = RefCell<Stdin>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for StdinWrapper { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Api for StdinWrapper { - - fn project<N: AsRef<str>>(&self, _name: N) -> Result<Vec<Package>> { - let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; - serde_json::de::from_str(&s).map_err(Error::from) - } - - fn problems_for_repo<R: AsRef<str>>(&self, _repo: R) -> Result<Vec<Problem>> { - let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; - serde_json::de::from_str(&s).map_err(Error::from) - } - - fn problems_for_maintainer<M: AsRef<str>>(&self, _maintainer: M) -> Result<Vec<Problem>> { - let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; - serde_json::de::from_str(&s).map_err(Error::from) - } - -} - -fn read_to_string(input: &mut Read) -> Result<String> { - let mut buffer = String::new(); - let read = input.read_to_string(&mut buffer)?; - trace!("Read {} bytes from stdin", read); - Ok(buffer) -} diff --git a/librepology/src/v1/mod.rs b/librepology/src/v1/mod.rs index cd4d2fe..1d5eed6 100644 --- a/librepology/src/v1/mod.rs +++ b/librepology/src/v1/mod.rs @@ -1,3 +1,4 @@ pub mod restapi; +pub mod stdinapi; pub mod api; pub mod types; diff --git a/librepology/src/v1/restapi.rs b/librepology/src/v1/restapi.rs index df26135..bc1a693 100644 --- a/librepology/src/v1/restapi.rs +++ b/librepology/src/v1/restapi.rs @@ -7,6 +7,7 @@ use curl::easy::Easy2; use crate::v1::types::{Package, Problem}; use crate::v1::api::Api; +/// Private helper type for collecting data from the curl library struct Collector(Vec<u8>); impl curl::easy::Handler for Collector { fn write(&mut self, data: &[u8]) -> RResult<usize, curl::easy::WriteError> { @@ -15,6 +16,7 @@ impl curl::easy::Handler for Collector { } } +/// Representational object for the REST Api of repology pub struct RestApi { /// Base url repology: String, @@ -25,6 +27,7 @@ impl RestApi { Self { repology } } + /// Helper function for sending a request via the curl library fn send_request<U: AsRef<str>>(&self, request: U) -> Result<String> { let mut easy = Easy2::new(Collector(Vec::new())); easy.get(true)?; @@ -38,30 +41,18 @@ impl RestApi { impl Api for RestApi { fn project<N: AsRef<str>>(&self, name: N) -> Result<Vec<Package>> { - let request_url = format!("{}api/v1/project/{}", self.repology, name.as_ref()); - - self.send_request(request_url) - .and_then(|r| { - serde_json::from_str(&r).map_err(Error::from) - }) + let url = format!("{}api/v1/project/{}", self.repology, name.as_ref()); + serde_json::from_str(&self.send_request(url)?).map_err(Error::from) } fn problems_for_repo<R: AsRef<str>>(&self, repo: R) -> Result<Vec<Problem>> { - let request_url = format!("{}api/v1/repository/{}/problems", self.repology, repo.as_ref()); - - self.send_request(request_url) - .and_then(|r| { - serde_json::from_str(&r).map_err(Error::from) - }) + let url = format!("{}api/v1/repository/{}/problems", self.repology, repo.as_ref()); + serde_json::from_str(&self.send_request(url)?).map_err(Error::from) } fn problems_for_maintainer<M: AsRef<str>>(&self, maintainer: M) -> Result<Vec<Problem>> { - let request_url = format!("{}api/v1/maintainer/{}/problems", self.repology, maintainer.as_ref()); - - self.send_request(request_url) - .and_then(|r| { - serde_json::from_str(&r).map_err(Error::from) - }) + let url = format!("{}api/v1/maintainer/{}/problems", self.repology, maintainer.as_ref()); + serde_json::from_str(&self.send_request(url)?).map_err(Error::from) } }
\ No newline at end of file diff --git a/librepology/src/v1/stdinapi.rs b/librepology/src/v1/stdinapi.rs new file mode 100644 index 0000000..8ee26a4 --- /dev/null +++ b/librepology/src/v1/stdinapi.rs @@ -0,0 +1,63 @@ +use std::io::{Stdin, Read}; +use std::cell::RefCell; +use std::ops::Deref; +use std::ops::DerefMut; + +use failure::Fallible as Result; +use failure::Error; + +use crate::v1::types::Problem; +use crate::v1::types::Package; +use crate::v1::api::Api; + +/// Wrapper for "stdin" +/// +/// This way we can implement the `Api` trait for StdIn (via a Wrapper for interior mutability) +/// This way we can read the data from stdin and process it. +pub struct StdinWrapper(RefCell<Stdin>); + +impl From<Stdin> for StdinWrapper { + fn from(inner: Stdin) -> Self { + StdinWrapper(RefCell::new(inner)) + } +} + +impl Deref for StdinWrapper { + type Target = RefCell<Stdin>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StdinWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Api for StdinWrapper { + + fn project<N: AsRef<str>>(&self, _name: N) -> Result<Vec<Package>> { + let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; + serde_json::de::from_str(&s).map_err(Error::from) + } + + fn problems_for_repo<R: AsRef<str>>(&self, _repo: R) -> Result<Vec<Problem>> { + let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; + serde_json::de::from_str(&s).map_err(Error::from) + } + + fn problems_for_maintainer<M: AsRef<str>>(&self, _maintainer: M) -> Result<Vec<Problem>> { + let s = read_to_string(self.0.try_borrow_mut()?.deref_mut())?; + serde_json::de::from_str(&s).map_err(Error::from) + } + +} + +fn read_to_string(input: &mut Read) -> Result<String> { + let mut buffer = String::new(); + let read = input.read_to_string(&mut buffer)?; + trace!("Read {} bytes from stdin", read); + Ok(buffer) +} diff --git a/librepology/src/v1/types/mod.rs b/librepology/src/v1/types/mod.rs index 4e97b44..f192c49 100644 --- a/librepology/src/v1/types/mod.rs +++ b/librepology/src/v1/types/mod.rs @@ -1,3 +1,11 @@ +//! Module containing all _types_ of data for the API implementation +//! +//! Tne types have no functionality themselves but only represent objects which are returned by theĀ“ +//! repology API. +//! +//! This top-level module exports all types of the submodules publicly. +//! + mod category; mod download; mod effname; diff --git a/src/backend.rs b/src/backend.rs index 57afd10..fe1fa7e 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -3,8 +3,8 @@ use failure::Fallible as Result; use librepology::v1::api::Api; use librepology::v1::restapi::RestApi; +use librepology::v1::stdinapi::StdinWrapper; use librepology::v1::types::*; -use librepology::v1::api::StdinWrapper; use crate::config::Configuration; @@ -15,6 +15,9 @@ pub enum Backend { 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 { diff --git a/src/frontend/json.rs b/src/frontend/json.rs index cd7e651..39e86f8 100644 --- a/src/frontend/json.rs +++ b/src/frontend/json.rs @@ -15,23 +15,34 @@ use librepology::v1::api::Api; 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<()> { - let output = serde_json::ser::to_string_pretty(&packages).map_err(Error::from)?; - let mut outlock = self.0.lock(); - writeln!(outlock, "{}", output).map_err(Error::from) + self.write(serde_json::ser::to_string_pretty(&packages).map_err(Error::from)?) + } fn list_problems(&self, problems: Vec<Problem>) -> Result<()> { - let output = serde_json::ser::to_string_pretty(&problems).map_err(Error::from)?; - let mut outlock = self.0.lock(); - writeln!(outlock, "{}", output).map_err(Error::from) + self.write(serde_json::ser::to_string_pretty(&problems).map_err(Error::from)?) } fn compare_packages(&self, packages: Vec<ComparePackage>, backend: &Backend, filter_repos: Vec<Repo>) -> Result<()> { @@ -71,10 +82,7 @@ impl Frontend for JsonFrontend { }); } - let output = serde_json::ser::to_string_pretty(&output)?; - - let mut outlock = self.0.lock(); - writeln!(outlock, "{}", output).map_err(Error::from) + self.write(serde_json::ser::to_string_pretty(&output)?) } } diff --git a/src/frontend/list.rs b/src/frontend/list.rs index e870ab0..e4ed7d7 100644 --- a/src/frontend/list.rs +++ b/src/frontend/list.rs @@ -15,6 +15,9 @@ use librepology::v1::api::Api; 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) diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 43e5b5e..3bb912c 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -10,6 +10,7 @@ use crate::frontend::table::TableFrontend; use crate::compare::ComparePackage; use crate::backend::Backend; +/// 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<()>; @@ -20,6 +21,7 @@ pub mod list; pub mod json; 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<Frontend>> { match app.value_of("output") { None | Some("lines") => { diff --git a/src/frontend/table.rs b/src/frontend/table.rs index 6a30e9c..35c867c 100644 --- a/src/frontend/table.rs +++ b/src/frontend/table.rs @@ -13,16 +13,15 @@ use crate::backend::Backend; use crate::compare::ComparePackage; use librepology::v1::api::Api; +/// 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) } -} -impl Frontend for TableFrontend { - fn list_packages(&self, packages: Vec<Package>) -> Result<()> { + fn mktable(&self) -> Table { let mut table = Table::new(); let format = format::FormatBuilder::new() .column_separator('|') @@ -34,9 +33,20 @@ impl Frontend for TableFrontend { .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) @@ -56,28 +66,11 @@ impl Frontend for TableFrontend { table.add_row(row![package.name(), package.version(), package.repo(), status, url]); }); - - let mut outlock = self.0.lock(); - table.print(&mut outlock)?; - - Ok(()) + self.print(table) } fn list_problems(&self, problems: Vec<Problem>) -> 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!["Repo", "Name", "EffName", "Maintainer", "Description"]); - + let mut table = self.mktable(); problems.iter().for_each(|problem| { trace!("Adding row for: {:?}", problem); table.add_row(row![ @@ -88,28 +81,11 @@ impl Frontend for TableFrontend { problem.problem_description() ]); }); - - let mut outlock = self.0.lock(); - table.print(&mut outlock)?; - - Ok(()) + self.print(table) } 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"]); - + let mut table = self.mktable(); for package in packages { backend .project(package.name().deref())? @@ -124,11 +100,7 @@ impl Frontend for TableFrontend { ]); }); } - - let mut outlock = self.0.lock(); - table.print(&mut outlock)?; - - Ok(()) + self.print(table) } } |