diff --git a/benches/repo.rs b/benches/repo.rs index d1cc06960..05d552e31 100644 --- a/benches/repo.rs +++ b/benches/repo.rs @@ -1,6 +1,6 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use gix::{open, ThreadSafeRepository}; -use onefetch::{cli::CliOptions, info::Info}; +use onefetch::{cli::CliOptions, info::build_info}; fn bench_repo_info(c: &mut Criterion) { let name = "repo.sh".to_string(); @@ -13,7 +13,7 @@ fn bench_repo_info(c: &mut Criterion) { c.bench_function("get repo information", |b| { b.iter(|| { - let result = black_box(Info::new(&config)); + let result = black_box(build_info(&config)); assert!(result.is_ok()); }) }); diff --git a/src/info/author.rs b/src/info/author.rs index e7843f2b9..c9bea46b3 100644 --- a/src/info/author.rs +++ b/src/info/author.rs @@ -1,10 +1,6 @@ use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::git::Commits, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::git::Commits, utils::info_field::InfoField}, }; use owo_colors::{DynColors, OwoColorize}; use serde::Serialize; @@ -76,7 +72,7 @@ pub struct AuthorsInfo { } impl AuthorsInfo { - pub fn new(info_color: DynColors, commits: &mut Commits) -> Self { + pub fn new(info_color: DynColors, commits: &Commits) -> Self { let authors = commits.authors_to_display.clone(); Self { authors, @@ -119,10 +115,6 @@ impl InfoField for AuthorsInfo { title } - fn r#type(&self) -> InfoType { - InfoType::Authors - } - fn should_color(&self) -> bool { false } diff --git a/src/info/commits.rs b/src/info/commits.rs index 80e0922eb..cfee20cca 100644 --- a/src/info/commits.rs +++ b/src/info/commits.rs @@ -2,11 +2,7 @@ use serde::Serialize; use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::git::Commits, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::git::Commits, utils::info_field::InfoField}, }; #[derive(Serialize)] @@ -41,10 +37,6 @@ impl InfoField for CommitsInfo { fn title(&self) -> String { "Commits".into() } - - fn r#type(&self) -> InfoType { - InfoType::Commits - } } #[cfg(test)] diff --git a/src/info/contributors.rs b/src/info/contributors.rs index be9ed4415..e5b16429e 100644 --- a/src/info/contributors.rs +++ b/src/info/contributors.rs @@ -2,11 +2,7 @@ use serde::Serialize; use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::git::Commits, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::git::Commits, utils::info_field::InfoField}, }; #[derive(Serialize)] @@ -46,10 +42,6 @@ impl InfoField for ContributorsInfo { fn title(&self) -> String { "Contributors".into() } - - fn r#type(&self) -> InfoType { - InfoType::Contributors - } } #[cfg(test)] diff --git a/src/info/created.rs b/src/info/created.rs index 95b5846f5..af95a5e26 100644 --- a/src/info/created.rs +++ b/src/info/created.rs @@ -1,8 +1,5 @@ use super::utils::format_time; -use crate::info::{ - utils::git::Commits, - utils::info_field::{InfoField, InfoType}, -}; +use crate::info::{utils::git::Commits, utils::info_field::InfoField}; use serde::Serialize; #[derive(Serialize)] @@ -31,10 +28,6 @@ impl InfoField for CreatedInfo { fn title(&self) -> String { "Created".into() } - - fn r#type(&self) -> InfoType { - InfoType::Created - } } #[cfg(test)] diff --git a/src/info/dependencies.rs b/src/info/dependencies.rs index c1dfd510b..5fb6e40aa 100644 --- a/src/info/dependencies.rs +++ b/src/info/dependencies.rs @@ -1,9 +1,6 @@ use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::info_field::InfoField}, }; use onefetch_manifest::Manifest; use serde::Serialize; @@ -40,10 +37,6 @@ impl InfoField for DependenciesInfo { fn title(&self) -> String { "Dependencies".into() } - - fn r#type(&self) -> InfoType { - InfoType::Dependencies - } } #[cfg(test)] diff --git a/src/info/description.rs b/src/info/description.rs index c9673a0e5..540b62e4e 100644 --- a/src/info/description.rs +++ b/src/info/description.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use onefetch_manifest::Manifest; use serde::Serialize; use std::fmt::Write; @@ -50,10 +50,6 @@ impl InfoField for DescriptionInfo { fn title(&self) -> String { "Description".into() } - - fn r#type(&self) -> InfoType { - InfoType::Description - } } #[cfg(test)] diff --git a/src/info/head.rs b/src/info/head.rs index 525f133a7..146d83867 100644 --- a/src/info/head.rs +++ b/src/info/head.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use anyhow::{Context, Result}; use gix::{reference::Category, Reference, Repository}; use serde::Serialize; @@ -72,10 +72,6 @@ impl InfoField for HeadInfo { fn title(&self) -> String { "HEAD".into() } - - fn r#type(&self) -> InfoType { - InfoType::Head - } } #[cfg(test)] diff --git a/src/info/langs/language.rs b/src/info/langs/language.rs index 6d20414f7..9dcd3a44b 100644 --- a/src/info/langs/language.rs +++ b/src/info/langs/language.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use owo_colors::OwoColorize; use serde::Serialize; use tokei; @@ -158,9 +158,6 @@ impl InfoField for LanguagesInfo { title } - fn r#type(&self) -> InfoType { - InfoType::Languages - } fn should_color(&self) -> bool { false } diff --git a/src/info/last_change.rs b/src/info/last_change.rs index 8cd214687..4055d11b1 100644 --- a/src/info/last_change.rs +++ b/src/info/last_change.rs @@ -1,8 +1,5 @@ use super::utils::format_time; -use crate::info::{ - utils::git::Commits, - utils::info_field::{InfoField, InfoType}, -}; +use crate::info::{utils::git::Commits, utils::info_field::InfoField}; use serde::Serialize; #[derive(Serialize)] @@ -32,10 +29,6 @@ impl InfoField for LastChangeInfo { fn title(&self) -> String { "Last change".into() } - - fn r#type(&self) -> InfoType { - InfoType::LastChange - } } #[cfg(test)] diff --git a/src/info/license.rs b/src/info/license.rs index 6095bf574..65dbc10bd 100644 --- a/src/info/license.rs +++ b/src/info/license.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use anyhow::{bail, Result}; use askalono::{Store, TextData}; use onefetch_manifest::Manifest; @@ -95,10 +95,6 @@ impl InfoField for LicenseInfo { fn title(&self) -> String { "License".into() } - - fn r#type(&self) -> InfoType { - InfoType::License - } } #[cfg(test)] diff --git a/src/info/loc.rs b/src/info/loc.rs index e6bf11378..9b47af1b2 100644 --- a/src/info/loc.rs +++ b/src/info/loc.rs @@ -4,10 +4,7 @@ use serde::Serialize; use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::info_field::InfoField}, }; #[derive(Serialize)] @@ -37,10 +34,6 @@ impl InfoField for LocInfo { fn title(&self) -> String { "Lines of code".into() } - - fn r#type(&self) -> InfoType { - InfoType::LinesOfCode - } } #[cfg(test)] diff --git a/src/info/mod.rs b/src/info/mod.rs index d8f09f31e..9adba561e 100644 --- a/src/info/mod.rs +++ b/src/info/mod.rs @@ -14,6 +14,7 @@ use self::pending::PendingInfo; use self::project::ProjectInfo; use self::size::SizeInfo; use self::title::Title; +use self::url::get_repo_url; use self::url::UrlInfo; use self::utils::git::Commits; use self::utils::info_field::{InfoField, InfoType}; @@ -22,6 +23,7 @@ use crate::cli::{is_truecolor_terminal, CliOptions, NumberSeparator, When}; use crate::ui::get_ascii_colors; use crate::ui::text_colors::TextColors; use anyhow::{Context, Result}; +use gix::Repository; use num_format::ToFormattedString; use onefetch_manifest::Manifest; use owo_colors::{DynColors, OwoColorize, Style}; @@ -50,17 +52,13 @@ mod version; #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct Info { - title: Title, + title: Option, info_fields: Vec<Box<dyn InfoField>>, #[serde(skip_serializing)] - disabled_fields: Vec<InfoType>, - #[serde(skip_serializing)] text_colors: TextColors, #[serde(skip_serializing)] no_color_palette: bool, #[serde(skip_serializing)] - no_title: bool, - #[serde(skip_serializing)] no_bold: bool, #[serde(skip_serializing)] pub dominant_language: Language, @@ -68,24 +66,29 @@ pub struct Info { pub ascii_colors: Vec<DynColors>, } +struct InfoBuilder { + title: Option<Title>, + info_fields: Vec<Box<dyn InfoField>>, + disabled_fields: Vec<InfoType>, + no_title: bool, +} + impl std::fmt::Display for Info { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { //Title - if !self.no_title { - write!(f, "{}", self.title)?; + if let Some(title) = &self.title { + write!(f, "{}", title)?; } //Info lines - for info_field in self - .info_fields - .iter() - .filter(|x| x.has_value(&self.disabled_fields)) - { - self.write_styled_info_line( + for info_field in self.info_fields.iter().filter(|x| !x.value().is_empty()) { + write_styled_info_line( f, &info_field.title(), &info_field.value(), info_field.should_color(), + self.no_bold, + &self.text_colors, )? } @@ -109,170 +112,336 @@ impl std::fmt::Display for Info { } } -impl Info { - pub fn new(cli_options: &CliOptions) -> Result<Self> { - let git_repo = gix::ThreadSafeRepository::discover_opts( - &cli_options.input, - gix::discover::upwards::Options { - dot_git_only: true, - ..Default::default() - }, - Default::default(), - )? - .to_thread_local(); - let repo_path = get_work_dir(&git_repo)?; - - let loc_by_language_sorted_handle = std::thread::spawn({ - let ignored_directories = cli_options.info.exclude.clone(); - let language_types = cli_options.info.r#type.clone(); - let include_hidden = cli_options.info.include_hidden; - let workdir = repo_path.clone(); - move || { - langs::get_loc_by_language_sorted( - &workdir, - &ignored_directories, - &language_types, - include_hidden, - ) - } - }); - - let loc_by_language = loc_by_language_sorted_handle - .join() - .ok() - .context("BUG: panic in language statistics thread")??; - let dominant_language = langs::get_main_language(&loc_by_language); - let true_color = match cli_options.ascii.true_color { - When::Always => true, - When::Never => false, - When::Auto => is_truecolor_terminal(), - }; - let ascii_colors = get_ascii_colors( - &cli_options.ascii.ascii_language, - &dominant_language, - &cli_options.ascii.ascii_colors, - true_color, - ); +pub fn build_info(cli_options: &CliOptions) -> Result<Info> { + let repo = gix::ThreadSafeRepository::discover_opts( + &cli_options.input, + gix::discover::upwards::Options { + dot_git_only: true, + ..Default::default() + }, + Default::default(), + )? + .to_thread_local(); + let repo_path = get_work_dir(&repo)?; - let text_colors = - TextColors::new(&cli_options.text_formatting.text_colors, ascii_colors[0]); - let title = Title::new( - &git_repo, - text_colors.title, - text_colors.tilde, - text_colors.underline, - !cli_options.text_formatting.no_bold, - ); - let manifest = get_manifest(&repo_path)?; - let description = DescriptionInfo::new(manifest.as_ref()); - let pending = PendingInfo::new(&git_repo)?; - let repo_url = UrlInfo::new(&git_repo)?; - let project = ProjectInfo::new( - &git_repo, - &repo_url.repo_url, - manifest.as_ref(), - cli_options.text_formatting.number_separator, - )?; - let head = HeadInfo::new(&git_repo)?; - let version = VersionInfo::new(&git_repo, manifest.as_ref())?; - let size = SizeInfo::new(&git_repo, cli_options.text_formatting.number_separator); - let license = LicenseInfo::new(&repo_path, manifest.as_ref())?; - let mut commits = Commits::new( - git_repo, - cli_options.info.no_merges, - &cli_options.info.no_bots, - cli_options.info.number_of_authors, - cli_options.info.email, - cli_options.text_formatting.number_separator, - )?; - let created = CreatedInfo::new(cli_options.text_formatting.iso_time, &commits); - let languages = LanguagesInfo::new( + let loc_by_language_sorted_handle = std::thread::spawn({ + let ignored_directories = cli_options.info.exclude.clone(); + let language_types = cli_options.info.r#type.clone(); + let include_hidden = cli_options.info.include_hidden; + let workdir = repo_path.clone(); + move || { + langs::get_loc_by_language_sorted( + &workdir, + &ignored_directories, + &language_types, + include_hidden, + ) + } + }); + + let loc_by_language = loc_by_language_sorted_handle + .join() + .ok() + .context("BUG: panic in language statistics thread")??; + let manifest = get_manifest(&repo_path)?; + let repo_url = get_repo_url(&repo)?; + + let commits = Commits::new( + &repo, + cli_options.info.no_merges, + &cli_options.info.no_bots, + cli_options.info.number_of_authors, + cli_options.info.email, + cli_options.text_formatting.number_separator, + )?; + let true_color = match cli_options.ascii.true_color { + When::Always => true, + When::Never => false, + When::Auto => is_truecolor_terminal(), + }; + let dominant_language = langs::get_main_language(&loc_by_language); + let ascii_colors = get_ascii_colors( + &cli_options.ascii.ascii_language, + &dominant_language, + &cli_options.ascii.ascii_colors, + true_color, + ); + + let text_colors = TextColors::new(&cli_options.text_formatting.text_colors, ascii_colors[0]); + let no_bold = cli_options.text_formatting.no_bold; + let number_separator = cli_options.text_formatting.number_separator; + let iso_time = cli_options.text_formatting.iso_time; + let number_of_languages = cli_options.info.number_of_languages; + let number_of_authors = cli_options.info.number_of_authors; + + Ok(InfoBuilder::new(cli_options)? + .title(&repo, no_bold, &text_colors) + .project(&repo, &repo_url, &manifest, number_separator)? + .description(&manifest) + .head(&repo)? + .pending(&repo)? + .version(&repo, &manifest)? + .created(&commits, iso_time) + .languages( &loc_by_language, true_color, - cli_options.info.number_of_languages, - text_colors.info, - ); - let dependencies = DependenciesInfo::new( - manifest.as_ref(), - cli_options.text_formatting.number_separator, - ); - let authors = AuthorsInfo::new(text_colors.info, &mut commits); - let last_change = LastChangeInfo::new(cli_options.text_formatting.iso_time, &commits); - let contributors = ContributorsInfo::new( - &commits, - cli_options.info.number_of_authors, - cli_options.text_formatting.number_separator, - ); - let commits = CommitsInfo::new(&commits, cli_options.text_formatting.number_separator); - let lines_of_code = LocInfo::new( - &loc_by_language, - cli_options.text_formatting.number_separator, - ); + number_of_languages, + &text_colors, + ) + .dependencies(&manifest, number_separator) + .authors(&commits, &text_colors) + .last_change(&commits, iso_time) + .contributors(&commits, number_of_authors, number_separator) + .url(&repo_url) + .commits(&commits, number_separator) + .loc(&loc_by_language, number_separator) + .size(&repo, number_separator) + .license(&repo_path, &manifest)? + .build(cli_options, text_colors, dominant_language, ascii_colors)) +} - let info_fields: Vec<Box<dyn InfoField>> = vec![ - Box::new(project), - Box::new(description), - Box::new(head), - Box::new(pending), - Box::new(version), - Box::new(created), - Box::new(languages), - Box::new(dependencies), - Box::new(authors), - Box::new(last_change), - Box::new(contributors), - Box::new(repo_url), - Box::new(commits), - Box::new(lines_of_code), - Box::new(size), - Box::new(license), - ]; +impl InfoBuilder { + fn new(cli_options: &CliOptions) -> Result<Self> { Ok(Self { - title, - info_fields, + title: None, + info_fields: Vec::new(), disabled_fields: cli_options.info.disabled_fields.clone(), + no_title: cli_options.info.no_title, + }) + } + + fn title(mut self, repo: &Repository, no_bold: bool, text_colors: &TextColors) -> Self { + if !self.no_title { + let title = Title::new( + repo, + text_colors.title, + text_colors.tilde, + text_colors.underline, + !no_bold, + ); + self.title = Some(title); + } + self + } + + fn description(mut self, manifest: &Option<Manifest>) -> Self { + if !self.disabled_fields.contains(&InfoType::Description) { + let description = DescriptionInfo::new(manifest.as_ref()); + self.info_fields.push(Box::new(description)); + } + self + } + + fn pending(mut self, repo: &Repository) -> Result<Self> { + if !self.disabled_fields.contains(&InfoType::Pending) { + let pending = PendingInfo::new(repo)?; + self.info_fields.push(Box::new(pending)); + } + Ok(self) + } + + fn url(mut self, repo_url: &str) -> Self { + if !self.disabled_fields.contains(&InfoType::URL) { + let repo_url = UrlInfo::new(repo_url); + self.info_fields.push(Box::new(repo_url)); + } + self + } + + fn project( + mut self, + repo: &Repository, + repo_url: &str, + manifest: &Option<Manifest>, + number_separator: NumberSeparator, + ) -> Result<Self> { + if !self.disabled_fields.contains(&InfoType::Project) { + let project = ProjectInfo::new(repo, repo_url, manifest.as_ref(), number_separator)?; + self.info_fields.push(Box::new(project)); + } + Ok(self) + } + + fn head(mut self, repo: &Repository) -> Result<Self> { + if !self.disabled_fields.contains(&InfoType::Head) { + let head = HeadInfo::new(repo)?; + self.info_fields.push(Box::new(head)); + } + Ok(self) + } + + fn version(mut self, repo: &Repository, manifest: &Option<Manifest>) -> Result<Self> { + if !self.disabled_fields.contains(&InfoType::Version) { + let version = VersionInfo::new(repo, manifest.as_ref())?; + self.info_fields.push(Box::new(version)); + } + Ok(self) + } + + fn size(mut self, repo: &Repository, number_separator: NumberSeparator) -> Self { + if !self.disabled_fields.contains(&InfoType::Size) { + let size = SizeInfo::new(repo, number_separator); + self.info_fields.push(Box::new(size)); + } + self + } + + fn license(mut self, repo_path: &Path, manifest: &Option<Manifest>) -> Result<Self> { + if !self.disabled_fields.contains(&InfoType::License) { + let license = LicenseInfo::new(repo_path, manifest.as_ref())?; + self.info_fields.push(Box::new(license)); + } + Ok(self) + } + + fn created(mut self, commits: &Commits, iso_time: bool) -> Self { + if !self.disabled_fields.contains(&InfoType::Created) { + let created = CreatedInfo::new(iso_time, commits); + self.info_fields.push(Box::new(created)); + } + self + } + + fn languages( + mut self, + loc_by_language: &[(Language, usize)], + true_color: bool, + number_of_languages: usize, + text_colors: &TextColors, + ) -> Self { + if !self.disabled_fields.contains(&InfoType::Languages) { + let languages = LanguagesInfo::new( + loc_by_language, + true_color, + number_of_languages, + text_colors.info, + ); + self.info_fields.push(Box::new(languages)); + } + self + } + + fn dependencies( + mut self, + manifest: &Option<Manifest>, + number_separator: NumberSeparator, + ) -> Self { + if !self.disabled_fields.contains(&InfoType::Dependencies) { + let dependencies = DependenciesInfo::new(manifest.as_ref(), number_separator); + self.info_fields.push(Box::new(dependencies)); + } + self + } + + fn authors(mut self, commits: &Commits, text_colors: &TextColors) -> Self { + if !self.disabled_fields.contains(&InfoType::Authors) { + let authors = AuthorsInfo::new(text_colors.info, commits); + self.info_fields.push(Box::new(authors)); + } + self + } + + fn last_change(mut self, commits: &Commits, iso_time: bool) -> Self { + if !self.disabled_fields.contains(&InfoType::LastChange) { + let last_change = LastChangeInfo::new(iso_time, commits); + self.info_fields.push(Box::new(last_change)); + } + self + } + + fn contributors( + mut self, + commits: &Commits, + number_of_authors: usize, + number_separator: NumberSeparator, + ) -> Self { + if !self.disabled_fields.contains(&InfoType::Contributors) { + let contributors = ContributorsInfo::new(commits, number_of_authors, number_separator); + self.info_fields.push(Box::new(contributors)); + } + self + } + + fn commits(mut self, commits: &Commits, number_separator: NumberSeparator) -> Self { + if !self.disabled_fields.contains(&InfoType::Commits) { + let commits = CommitsInfo::new(commits, number_separator); + self.info_fields.push(Box::new(commits)); + } + self + } + + fn loc( + mut self, + loc_by_language: &[(Language, usize)], + number_separator: NumberSeparator, + ) -> Self { + if !self.disabled_fields.contains(&InfoType::LinesOfCode) { + let lines_of_code = LocInfo::new(loc_by_language, number_separator); + self.info_fields.push(Box::new(lines_of_code)); + } + self + } + + fn build( + self, + cli_options: &CliOptions, + text_colors: TextColors, + dominant_language: Language, + ascii_colors: Vec<DynColors>, + ) -> Info { + Info { + title: self.title, + info_fields: self.info_fields, text_colors, dominant_language, ascii_colors, no_color_palette: cli_options.visuals.no_color_palette, - no_title: cli_options.info.no_title, no_bold: cli_options.text_formatting.no_bold, - }) + } } +} - fn write_styled_info_line( - &self, - f: &mut std::fmt::Formatter, - subtitle: &str, - info: &str, - should_color_info: bool, - ) -> std::fmt::Result { - writeln!( - f, - "{} {}", - &self.style_subtitle(subtitle), - &self.style_info(info, should_color_info) - ) - } +fn write_styled_info_line( + f: &mut std::fmt::Formatter, + subtitle: &str, + info: &str, + should_color_info: bool, + no_bold: bool, + text_colors: &TextColors, +) -> std::fmt::Result { + writeln!( + f, + "{} {}", + style_subtitle(subtitle, text_colors, no_bold), + style_info(info, text_colors, should_color_info) + ) +} - fn style_info(&self, info: &str, with_color: bool) -> String { - if with_color { - let info_style = get_style(false, self.text_colors.info); - format!("{}", info.style(info_style)) - } else { - info.into() - } +fn style_info(info: &str, text_colors: &TextColors, with_color: bool) -> String { + if with_color { + let info_style = get_style(false, text_colors.info); + format!("{}", info.style(info_style)) + } else { + info.into() } +} - fn style_subtitle(&self, subtitle: &str) -> String { - let subtitle_style = get_style(!self.no_bold, self.text_colors.subtitle); - let colon_style = get_style(!self.no_bold, self.text_colors.colon); - format!( - "{}{}", - subtitle.style(subtitle_style), - ":".style(colon_style) - ) +fn style_subtitle(subtitle: &str, text_colors: &TextColors, no_bold: bool) -> String { + let subtitle_style = get_style(!no_bold, text_colors.subtitle); + let colon_style = get_style(!no_bold, text_colors.colon); + format!( + "{}{}", + subtitle.style(subtitle_style), + ":".style(colon_style) + ) +} + +fn get_style(is_bold: bool, color: DynColors) -> Style { + let mut style = Style::new().color(color); + if is_bold { + style = style.bold(); } + style } fn get_manifest(repo_path: &Path) -> Result<Option<Manifest>> { @@ -299,18 +468,8 @@ fn format_number<T: ToFormattedString + std::fmt::Display>( number.to_formatted_string(&number_separator.get_format()) } -fn get_style(is_bold: bool, color: DynColors) -> Style { - let mut style = Style::new().color(color); - if is_bold { - style = style.bold(); - } - style -} - #[cfg(test)] mod tests { - use crate::cli::TextForamttingCliOptions; - use super::*; use owo_colors::AnsiColors; @@ -350,20 +509,14 @@ mod tests { #[test] fn test_info_style_info() -> Result<()> { - let config: CliOptions = CliOptions { - text_formatting: TextForamttingCliOptions { - text_colors: vec![0, 0, 0, 0, 0, 0], - ..Default::default() - }, - ..Default::default() - }; + let text_colors = + TextColors::new(&vec![0, 0, 0, 0, 0, 0], DynColors::Ansi(AnsiColors::Blue)); - let info = Info::new(&config)?; - let info_text = info.style_info("foo", false); + let info_text = style_info("foo", &text_colors, false); assert_eq!(info_text, "foo"); // Should display colour code - let info_text = info.style_info("foo", true); + let info_text = style_info("foo", &text_colors, true); // Rendered text: black `foo` assert_eq!(info_text, "\u{1b}[30mfoo\u{1b}[0m"); Ok(()) @@ -371,17 +524,10 @@ mod tests { #[test] fn test_info_style_subtitle() -> Result<()> { - let config: CliOptions = CliOptions { - text_formatting: TextForamttingCliOptions { - text_colors: vec![0, 0, 0, 0, 15, 0], - no_bold: false, - ..Default::default() - }, - ..Default::default() - }; + let text_colors = + TextColors::new(&vec![0, 0, 0, 0, 15, 0], DynColors::Ansi(AnsiColors::Blue)); - let info = Info::new(&config)?; - let subtitle_text = info.style_subtitle("foo"); + let subtitle_text = style_subtitle("foo", &text_colors, false); assert_eq!( subtitle_text, // Rendered text: black `foo` and bright white colon diff --git a/src/info/pending.rs b/src/info/pending.rs index bb8eba3fb..e10200b07 100644 --- a/src/info/pending.rs +++ b/src/info/pending.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use anyhow::Result; use git2::{Status, StatusOptions, StatusShow}; use gix::Repository; @@ -68,10 +68,6 @@ impl InfoField for PendingInfo { fn title(&self) -> String { "Pending".into() } - - fn r#type(&self) -> InfoType { - InfoType::Pending - } } #[cfg(test)] diff --git a/src/info/project.rs b/src/info/project.rs index ef16e0ac9..f5eca065f 100644 --- a/src/info/project.rs +++ b/src/info/project.rs @@ -1,9 +1,6 @@ use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::info_field::InfoField}, }; use anyhow::Result; use gix::{bstr::ByteSlice, Repository}; @@ -118,10 +115,6 @@ impl InfoField for ProjectInfo { fn title(&self) -> String { "Project".into() } - - fn r#type(&self) -> InfoType { - InfoType::Project - } } #[cfg(test)] diff --git a/src/info/size.rs b/src/info/size.rs index f3bf4c0f6..b6bd87914 100644 --- a/src/info/size.rs +++ b/src/info/size.rs @@ -1,9 +1,6 @@ use crate::{ cli::NumberSeparator, - info::{ - format_number, - utils::info_field::{InfoField, InfoType}, - }, + info::{format_number, utils::info_field::InfoField}, }; use byte_unit::Byte; use gix::Repository; @@ -71,10 +68,6 @@ impl InfoField for SizeInfo { fn title(&self) -> String { "Size".into() } - - fn r#type(&self) -> InfoType { - InfoType::Size - } } #[cfg(test)] diff --git a/src/info/url.rs b/src/info/url.rs index 82a738ec6..0fe9c22e5 100644 --- a/src/info/url.rs +++ b/src/info/url.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use anyhow::Result; use gix::Repository; use regex::Regex; @@ -10,13 +10,14 @@ pub struct UrlInfo { pub repo_url: String, } impl UrlInfo { - pub fn new(repo: &Repository) -> Result<Self> { - let repo_url = get_url(repo)?; - Ok(Self { repo_url }) + pub fn new(repo_url: &str) -> Self { + Self { + repo_url: repo_url.into(), + } } } -fn get_url(repo: &Repository) -> Result<String> { +pub fn get_repo_url(repo: &Repository) -> Result<String> { let config = repo.config_snapshot(); let remotes = match config.plumbing().sections_by_name("remote") { Some(sections) => sections, @@ -58,10 +59,6 @@ impl InfoField for UrlInfo { fn title(&self) -> String { "URL".into() } - - fn r#type(&self) -> InfoType { - InfoType::URL - } } #[cfg(test)] diff --git a/src/info/utils/git.rs b/src/info/utils/git.rs index ff205f5a1..967c644f9 100644 --- a/src/info/utils/git.rs +++ b/src/info/utils/git.rs @@ -31,17 +31,13 @@ impl From<gix::actor::Signature> for Sig { impl Commits { pub fn new( - mut repo: gix::Repository, + repo: &gix::Repository, no_merges: bool, no_bots: &Option<Option<MyRegex>>, number_of_authors_to_display: usize, show_email: bool, number_separator: NumberSeparator, ) -> Result<Self> { - // assure that objects we just traversed are coming from cache - // when we read the commit right after. - repo.object_cache_size(32 * 1024); - let bot_regex_pattern = get_no_bots_regex(no_bots)?; let mut time_of_most_recent_commit = None; let mut time_of_first_commit = None; diff --git a/src/info/utils/info_field.rs b/src/info/utils/info_field.rs index 8b54c1e25..bcaa445be 100644 --- a/src/info/utils/info_field.rs +++ b/src/info/utils/info_field.rs @@ -2,13 +2,9 @@ pub trait InfoField { fn value(&self) -> String; fn title(&self) -> String; - fn r#type(&self) -> InfoType; fn should_color(&self) -> bool { true } - fn has_value(&self, disabled_infos: &[InfoType]) -> bool { - !(disabled_infos.contains(&self.r#type()) || self.value().is_empty()) - } } #[derive(Clone, clap::ValueEnum, Debug, Eq, PartialEq)] @@ -49,30 +45,12 @@ mod test { fn title(&self) -> String { "title".into() } - - fn r#type(&self) -> InfoType { - InfoType::Project - } } #[test] fn test_info_field_with_value() { let info = InfoFieldImpl("test"); - assert_eq!(info.has_value(&[]), true); assert_eq!(info.title(), "title".to_string()); - assert_eq!(info.r#type(), InfoType::Project); assert_eq!(info.value(), "test".to_string()); } - - #[test] - fn test_info_field_when_type_is_disabled() { - let info = InfoFieldImpl("test"); - assert_eq!(info.has_value(&[InfoType::Project]), false); - } - - #[test] - fn test_info_field_without_value() { - let info = InfoFieldImpl(""); - assert_eq!(info.has_value(&[]), false); - } } diff --git a/src/info/version.rs b/src/info/version.rs index 7170c15ed..d3f3026b4 100644 --- a/src/info/version.rs +++ b/src/info/version.rs @@ -1,4 +1,4 @@ -use crate::info::utils::info_field::{InfoField, InfoType}; +use crate::info::utils::info_field::InfoField; use anyhow::Result; use gix::Repository; use onefetch_manifest::Manifest; @@ -50,10 +50,6 @@ impl InfoField for VersionInfo { fn title(&self) -> String { "Version".into() } - - fn r#type(&self) -> InfoType { - InfoType::Version - } } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 086579df5..e96e6972c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ use anyhow::Result; use clap::{CommandFactory, Parser}; use human_panic::setup_panic; -use onefetch::cli; -use onefetch::info::Info; +use onefetch::cli::{self, CliOptions}; +use onefetch::info::build_info; use onefetch::ui::printer::Printer; use std::io; @@ -25,12 +25,13 @@ fn main() -> Result<()> { } if let Some(generator) = cli_options.developer.completion { - let mut cmd = cli::CliOptions::command(); + let mut cmd = CliOptions::command(); cli::print_completions(generator, &mut cmd); return Ok(()); } - let info = Info::new(&cli_options)?; + let info = build_info(&cli_options)?; + let mut printer = Printer::new(io::BufWriter::new(io::stdout()), info, cli_options)?; printer.print()?; diff --git a/src/ui/text_colors.rs b/src/ui/text_colors.rs index 1c390c28f..c3d426646 100644 --- a/src/ui/text_colors.rs +++ b/src/ui/text_colors.rs @@ -1,6 +1,7 @@ use crate::ui::num_to_color; use owo_colors::{AnsiColors, DynColors}; +#[derive(Clone)] pub struct TextColors { pub title: DynColors, pub tilde: DynColors, diff --git a/tests/repo.rs b/tests/repo.rs index c6750505b..c1cc9c6e2 100644 --- a/tests/repo.rs +++ b/tests/repo.rs @@ -1,7 +1,7 @@ use anyhow::Result; use gix::{open, Repository, ThreadSafeRepository}; use onefetch::cli::{CliOptions, TextForamttingCliOptions}; -use onefetch::info::{get_work_dir, Info}; +use onefetch::info::{build_info, get_work_dir}; fn repo(name: &str) -> Result<Repository> { let name = name.to_string(); @@ -36,7 +36,7 @@ fn test_repo() -> Result<()> { }, ..Default::default() }; - let info = Info::new(&config)?; + let info = build_info(&config)?; insta::assert_json_snapshot!( info, { @@ -55,7 +55,7 @@ fn test_repo_without_remote() -> Result<()> { input: repo.path().to_path_buf(), ..Default::default() }; - let info = Info::new(&config); + let info = build_info(&config); assert!(info.is_ok()); Ok(())