Add colored builder that allows for turning color on and off

i18n
trivernis 2 years ago
parent 0a0268c19a
commit 6840689d77
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -0,0 +1,129 @@
use std::{borrow::Cow, fmt::Display};
use colored::{ColoredString, Colorize};
pub struct FmtBuilder<'a> {
options: FmtOptions,
parts: Vec<Part<'a>>,
}
pub enum Part<'a> {
Borrowed(&'a str),
Owned(String),
Colored(ColoredString),
Eval(Box<dyn Fn() -> Part<'a>>),
Empty,
}
#[derive(Default)]
pub struct FmtOptions {
pub colored: bool,
}
impl<'a> FmtBuilder<'a> {
/// Creates a new builder
pub fn new() -> Self {
Self {
options: FmtOptions::default(),
parts: Vec::new(),
}
}
pub fn options(&mut self, options: FmtOptions) -> &mut Self {
self.options = options;
self
}
pub fn append<S: Into<Part<'a>>>(&mut self, part: S) -> &mut Self {
self.parts.push(part.into());
self
}
pub fn append_if<F, S>(&mut self, condition: bool, string_fn: F) -> &mut Self
where
F: Fn() -> S,
S: Into<Part<'a>>,
{
if condition {
self.append(string_fn());
}
self
}
/// Builds a string representation.
pub fn build(&self) -> String {
let string_parts = self
.parts
.iter()
.filter_map(|c| c.fmt_string(&self.options))
.collect::<Vec<_>>();
let cap = string_parts.iter().map(|c| c.len()).sum();
string_parts
.into_iter()
.fold(String::with_capacity(cap), |mut acc, e| {
acc.push_str(e.as_ref());
acc
})
}
}
impl<'a> Display for FmtBuilder<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.build().fmt(f)
}
}
impl<'a> Part<'a> {
fn fmt_string(&self, opts: &FmtOptions) -> Option<Cow<'_, str>> {
match &self {
Part::Borrowed(b) => Some(Cow::Borrowed(b)),
Part::Owned(o) => Some(Cow::Borrowed(o)),
Part::Colored(c) => Some(Cow::Owned(Self::fmt_colored(c, opts))),
Part::Eval(e) => {
let part = e();
part.fmt_string(opts).map(Cow::into_owned).map(Cow::Owned)
}
Part::Empty => None,
}
}
fn fmt_colored(c: &ColoredString, opts: &FmtOptions) -> String {
if opts.colored {
c.to_string()
} else {
c.clone().clear().to_string()
}
}
}
impl<'a> Into<Part<'a>> for String {
fn into(self) -> Part<'a> {
Part::Owned(self)
}
}
impl<'a> Into<Part<'a>> for &'a str {
fn into(self) -> Part<'a> {
Part::Borrowed(self)
}
}
impl<'a> Into<Part<'a>> for &'a String {
fn into(self) -> Part<'a> {
Part::Borrowed(self)
}
}
impl<'a> Into<Part<'a>> for ColoredString {
fn into(self) -> Part<'a> {
Part::Colored(self)
}
}
impl<'a, P: Into<Part<'a>>> Into<Part<'a>> for Option<P> {
fn into(self) -> Part<'a> {
self.map(P::into).unwrap_or(Part::Empty)
}
}

@ -15,6 +15,7 @@ use fmt_layer::AmeFormatLayer;
use crate::internal::uwu_enabled;
use self::handler::LogHandler;
pub mod fmt_builder;
pub mod handler;
pub mod output;
pub mod piped_stdio;

@ -8,6 +8,8 @@ use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcsearch;
use crate::internal::utils::wrap_text;
use crate::logging::fmt_builder::FmtBuilder;
use crate::logging::fmt_builder::FmtOptions;
use crate::logging::Printable;
use crate::Options;
@ -32,99 +34,68 @@ impl PackageSearchResult {
pub fn score(&self, query: &str) -> f32 {
similarity(query, &self.name)
}
fn fmt_builder<'a>(&'a self) -> FmtBuilder<'a> {
let mut builder = FmtBuilder::new();
builder
.append({
let repo = self.repo.clone();
if repo == "aur" {
format!("{repo}/").bold().cyan()
} else {
format!("{repo}/").bold().purple()
}
})
.append(self.name.bold())
.append(" ")
.append(self.version.bold().green())
.append_if(
!self.groups.as_ref().map(|g| g.is_empty()).unwrap_or(true),
|| {
self.groups
.as_ref()
.map(|groups| format!("({})", groups.join(",")))
.unwrap()
.bold()
.blue()
},
)
.append(
self.out_of_date
.map(|ood| Local.timestamp(ood.try_into().unwrap(), 0).date_naive())
.map(|ood| format!(" [{} {}]", fl!("out-of-date"), ood).bold().red()),
)
.append_if(self.installed, || {
format!(" [{}]", fl!("installed")).bold().cyan()
})
.append("\n ")
.append(
wrap_text(
self.description
.clone()
.unwrap_or_else(|| "No description".to_string()),
4,
)
.join("\n"),
);
builder
}
}
impl Display for PackageSearchResult {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let repo = &self.repo;
let name = &self.name;
let version = &self.version;
let groups = if let Some(groups) = &self.groups {
if groups.is_empty() {
String::new()
} else {
format!("({})", groups.join(", "))
}
} else {
String::new()
};
let out_of_date = if let Some(out_of_date) = self.out_of_date {
format!(
" [{} {}]",
fl!("out-of-date"),
Local
.timestamp(out_of_date.try_into().unwrap(), 0)
.date_naive()
)
} else {
String::new()
};
let installed = if self.installed {
format!(" [{}]", fl!("installed"))
} else {
String::new()
};
let description = wrap_text(
self.description
.clone()
.unwrap_or_else(|| "No description".to_string()),
4,
)
.join("\n");
format!("{repo}{name} {version}{groups}{out_of_date}{installed}\n {description}").fmt(f)
self.fmt_builder()
.options(FmtOptions { colored: false })
.fmt(f)
}
}
impl Printable for PackageSearchResult {
fn to_print_string(&self) -> String {
let repo = if &self.repo == "aur" {
(self.repo.clone() + "/").bold().cyan()
} else {
(self.repo.clone() + "/").bold().purple()
};
let name = &self.name.bold();
let version = &self.version.bold().green();
let groups = if let Some(groups) = &self.groups {
if groups.is_empty() {
"".to_string()
} else {
format!(" ({})", groups.join(", "))
}
} else {
"".to_string()
}
.bold()
.blue();
let out_of_date = if let Some(out_of_date) = self.out_of_date {
format!(
" [{} {}]",
fl!("out-of-date"),
Local
.timestamp(out_of_date.try_into().unwrap(), 0)
.date_naive()
)
} else {
"".to_string()
}
.bold()
.red();
let installed = if self.installed {
format!(" [{}]", fl!("installed"))
} else {
"".to_string()
}
.bold()
.cyan();
let description = wrap_text(
self.description
.clone()
.unwrap_or_else(|| "No description".to_string()),
4,
)
.join("\n");
format!("{repo}{name} {version}{groups}{out_of_date}{installed}\n {description}")
self.fmt_builder()
.options(FmtOptions { colored: true })
.build()
}
}

Loading…
Cancel
Save