From 8ab2690d179884785a1f21859ecbf245896870f9 Mon Sep 17 00:00:00 2001 From: Thomas Eppers Date: Thu, 4 Aug 2022 15:37:19 +0200 Subject: [PATCH] added anyhow and thiserror; fixed clippy warnings; stopped creating switcher twice --- Cargo.lock | 28 +++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 13 ++++--- src/repository/dockerhub.rs | 10 +++--- src/repository/mod.rs | 18 +++------- src/ui/default.rs | 71 +++++++++++++++++++------------------ src/ui/mod.rs | 19 +++++----- src/ui/no_yaml.rs | 60 +++++++++++++++---------------- 8 files changed, 125 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0fac4a..11eaf1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91f1f46651137be86f3a2b9a8359f9ab421d04d941c62b5982e1ca21113adf9" + [[package]] name = "atty" version = "0.2.14" @@ -675,6 +681,7 @@ dependencies = [ name = "reel-moby" version = "1.2.1" dependencies = [ + "anyhow", "chrono", "lazy_static", "regex", @@ -683,6 +690,7 @@ dependencies = [ "serde_json", "structopt", "termion", + "thiserror", "tui", ] @@ -921,6 +929,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" diff --git a/Cargo.toml b/Cargo.toml index 8692a64..7a751ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ termion = "1.5" regex = "1.5.4" lazy_static = "1.4.0" structopt = "0.3.23" +thiserror = "1.0.32" +anyhow = "1.0.59" [profile.release] lto = "yes" diff --git a/src/main.rs b/src/main.rs index 1b5b1d1..982fd04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ -use std::path::PathBuf; -use structopt::StructOpt; - mod common; mod repo; mod repository; mod ui; mod widget; +use anyhow::Result; +use std::path::PathBuf; +use structopt::StructOpt; + /// helps you searching or updating tags of your used docker images #[derive(StructOpt, Debug)] pub struct Opt { @@ -19,8 +20,10 @@ pub struct Opt { repo: Option, } -fn main() { +fn main() -> Result<()> { //parse parameter let opt = Opt::from_args(); - ui::create_ui(&opt); + ui::create_ui(&opt)?; + + Ok(()) } diff --git a/src/repository/dockerhub.rs b/src/repository/dockerhub.rs index df043d5..ed3b587 100644 --- a/src/repository/dockerhub.rs +++ b/src/repository/dockerhub.rs @@ -19,11 +19,11 @@ pub struct Images { } impl Images { - pub fn convert(&self) -> super::Tag { + pub fn from_tag(images: &Self) -> super::Tag { super::Tag { - name: self.tag_name.clone(), - last_updated: Some(self.last_updated.clone()), - details: self + name: images.tag_name.clone(), + last_updated: Some(images.last_updated.clone()), + details: images .images .iter() .map(|d| super::TagDetails { @@ -69,7 +69,7 @@ impl DockerHub { } Ok(super::Repo { - tags: tags.results.iter().map(|t| t.convert()).collect(), + tags: tags.results.iter().map(Images::from_tag).collect(), next_page: tags.next_page, }) } diff --git a/src/repository/mod.rs b/src/repository/mod.rs index 34f5280..766d92a 100644 --- a/src/repository/mod.rs +++ b/src/repository/mod.rs @@ -1,32 +1,24 @@ mod dockerhub; -use std::fmt; - use chrono::DateTime; +use thiserror::Error; use crate::common::display_duration_ext::DisplayDurationExt; use crate::repo; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Error)] pub enum Error { /// couldn't fetch json with reqwest + #[error("Fetching error: {0}")] Fetching(String), /// a serde error + #[error("Converting error: {0}")] Converting(String), /// invalid repos show a valid json with 0 tags + #[error("Given Repo does not exists or has 0 tags.")] NoTagsFound, } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Fetching(s) => write!(f, "Fetching error: {}", s), - Error::Converting(s) => write!(f, "Converting error: {}", s), - Error::NoTagsFound => write!(f, "Given Repo has 0 tags. Is it valid?"), - } - } -} - #[derive(Clone, PartialEq)] pub struct TagDetails { pub arch: Option, diff --git a/src/ui/default.rs b/src/ui/default.rs index b7f5e7b..472e0b2 100644 --- a/src/ui/default.rs +++ b/src/ui/default.rs @@ -1,17 +1,18 @@ -use std::{io, thread}; - -use crate::Opt; +use anyhow::Result; use termion::event::Key; use termion::raw::IntoRawMode; use tui::backend::TermionBackend; use tui::layout::{Constraint, Direction, Layout}; use tui::Terminal; +use std::{io, thread}; + use crate::repository; use crate::widget::info; use crate::widget::repo_entry; use crate::widget::service_switcher; use crate::widget::tag_list; +use crate::Opt; pub struct Ui { state: State, @@ -53,14 +54,14 @@ impl std::iter::Iterator for State { } impl Ui { - pub fn run(opt: &Opt) { + pub fn run(opt: &Opt, switcher: service_switcher::ServiceSwitcher) -> Result<()> { let repo_id = opt.repo.as_deref(); let mut ui = Ui { state: State::SelectService, repo: repo_entry::RepoEntry::new(repo_id), tags: tag_list::TagList::with_status("Tags are empty"), - services: service_switcher::ServiceSwitcher::new(&opt.file).unwrap(), + services: switcher, details: crate::widget::details::Details::new(), info: info::Info::new("Select image of edit Repository"), }; @@ -70,9 +71,9 @@ impl Ui { } //setup tui - let stdout = io::stdout().into_raw_mode().unwrap(); + let stdout = io::stdout().into_raw_mode()?; let backend = TermionBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); + let mut terminal = Terminal::new(backend)?; //setup input thread let receiver = super::spawn_stdin_channel(); @@ -80,34 +81,32 @@ impl Ui { //core interaction loop 'core: loop { //draw - terminal - .draw(|rect| { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints( - [ - Constraint::Length(10), - Constraint::Length(3), - Constraint::Min(7), - Constraint::Length(2), - ] - .as_ref(), - ) - .split(rect.size()); + terminal.draw(|rect| { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + Constraint::Length(10), + Constraint::Length(3), + Constraint::Min(7), + Constraint::Length(2), + ] + .as_ref(), + ) + .split(rect.size()); - let (list, state) = ui.services.render(ui.state == State::SelectService); - rect.render_stateful_widget(list, chunks[0], state); - rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[1]); - let (list, state) = ui.tags.render(ui.state == State::SelectTag); - let more_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Min(15), Constraint::Length(28)].as_ref()) - .split(chunks[2]); - rect.render_stateful_widget(list, more_chunks[0], state); - rect.render_widget(ui.details.render(), more_chunks[1]); - rect.render_widget(ui.info.render(), chunks[3]); - }) - .unwrap(); + let (list, state) = ui.services.render(ui.state == State::SelectService); + rect.render_stateful_widget(list, chunks[0], state); + rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[1]); + let (list, state) = ui.tags.render(ui.state == State::SelectTag); + let more_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Min(15), Constraint::Length(28)].as_ref()) + .split(chunks[2]); + rect.render_stateful_widget(list, more_chunks[0], state); + rect.render_widget(ui.details.render(), more_chunks[1]); + rect.render_widget(ui.info.render(), chunks[3]); + })?; //handle input match receiver.try_recv() { @@ -219,6 +218,8 @@ impl Ui { thread::sleep(std::time::Duration::from_millis(32)); } - terminal.clear().unwrap(); + terminal.clear()?; + + Ok(()) } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 5b04af0..6e91e89 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,20 +1,23 @@ mod default; mod no_yaml; +use anyhow::Result; +use termion::input::TermRead; + +use crate::widget::service_switcher; +use crate::Opt; + use std::sync::mpsc; use std::{io, thread}; -use crate::Opt; -use termion::input::TermRead; - -use crate::widget::service_switcher; - -pub fn create_ui(opt: &Opt) { +pub fn create_ui(opt: &Opt) -> Result<()> { let service_result = service_switcher::ServiceSwitcher::new(&opt.file); match service_result { None => no_yaml::NoYaml::run(opt), - Some(_) => default::Ui::run(opt), - } + Some(switcher) => default::Ui::run(opt, switcher), + }?; + + Ok(()) } /// create a thread for catching input and send them to core loop diff --git a/src/ui/no_yaml.rs b/src/ui/no_yaml.rs index 9255383..4826102 100644 --- a/src/ui/no_yaml.rs +++ b/src/ui/no_yaml.rs @@ -1,11 +1,12 @@ -use std::{io, thread}; - +use anyhow::Result; use termion::event::Key; use termion::raw::IntoRawMode; use tui::backend::TermionBackend; use tui::layout::{Constraint, Direction, Layout}; use tui::Terminal; +use std::{io, thread}; + use crate::widget::details; use crate::widget::info; use crate::widget::repo_entry; @@ -48,7 +49,7 @@ pub struct NoYaml { } impl NoYaml { - pub fn run(opt: &Opt) { + pub fn run(opt: &Opt) -> Result<()> { let repo_id = opt.repo.as_deref(); let mut ui = NoYaml { @@ -65,9 +66,9 @@ impl NoYaml { } //setup tui - let stdout = io::stdout().into_raw_mode().unwrap(); + let stdout = io::stdout().into_raw_mode()?; let backend = TermionBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); + let mut terminal = Terminal::new(backend)?; //setup input thread let receiver = super::spawn_stdin_channel(); @@ -75,31 +76,29 @@ impl NoYaml { //core interaction loop 'core: loop { //draw - terminal - .draw(|rect| { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints( - [ - Constraint::Length(3), - Constraint::Min(7), - Constraint::Length(2), - ] - .as_ref(), - ) - .split(rect.size()); + terminal.draw(|rect| { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + Constraint::Length(3), + Constraint::Min(7), + Constraint::Length(2), + ] + .as_ref(), + ) + .split(rect.size()); - rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[0]); - let (list, state) = ui.tags.render(ui.state == State::SelectTag); - let more_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Min(15), Constraint::Length(28)].as_ref()) - .split(chunks[1]); - rect.render_stateful_widget(list, more_chunks[0], state); - rect.render_widget(ui.details.render(), more_chunks[1]); - rect.render_widget(ui.info.render(), chunks[2]); - }) - .unwrap(); + rect.render_widget(ui.repo.render(ui.state == State::EditRepo), chunks[0]); + let (list, state) = ui.tags.render(ui.state == State::SelectTag); + let more_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Min(15), Constraint::Length(28)].as_ref()) + .split(chunks[1]); + rect.render_stateful_widget(list, more_chunks[0], state); + rect.render_widget(ui.details.render(), more_chunks[1]); + rect.render_widget(ui.info.render(), chunks[2]); + })?; //handle input match receiver.try_recv() { @@ -156,6 +155,7 @@ impl NoYaml { thread::sleep(std::time::Duration::from_millis(32)); } - terminal.clear().unwrap(); + terminal.clear()?; + Ok(()) } }