use ratatui::{
style::{Modifier, Style},
text::{Line, Span},
};
use crate::{i18n, state::AppState, theme::Theme};
fn selected_aur_vote_state_text(app: &AppState) -> Option<String> {
let selected = app.results.get(app.selected)?;
if !matches!(selected.source, crate::state::Source::Aur) {
return None;
}
let pkgbase = &selected.name;
Some(match app.aur_vote_state_by_pkgbase.get(pkgbase) {
Some(crate::state::app_state::AurVoteStateUi::Loading) => "Loading...".to_string(),
Some(crate::state::app_state::AurVoteStateUi::Voted) => "Voted".to_string(),
Some(crate::state::app_state::AurVoteStateUi::NotVoted) => "Not voted".to_string(),
Some(crate::state::app_state::AurVoteStateUi::Error(message)) => {
format!("Error ({message})")
}
_ => return None,
})
}
pub fn format_details_lines(app: &AppState, _area_width: u16, th: &Theme) -> Vec<Line<'static>> {
fn kv(key: &str, val: String, th: &Theme) -> Line<'static> {
Line::from(vec![
Span::styled(
format!("{key}: "),
Style::default()
.fg(th.sapphire)
.add_modifier(Modifier::BOLD),
),
Span::styled(val, Style::default().fg(th.text)),
])
}
let d = &app.details;
let repo_display = if crate::index::is_manjaro_name_or_owner(&d.name, &d.owner) {
"manjaro".to_string()
} else {
d.repository.clone()
};
let mut lines = vec![
kv(
&i18n::t(app, "app.details.fields.repository"),
repo_display,
th,
),
kv(
&i18n::t(app, "app.details.fields.package_name"),
d.name.clone(),
th,
),
kv(
&i18n::t(app, "app.details.fields.version"),
d.version.clone(),
th,
),
kv(
&i18n::t(app, "app.details.fields.description"),
d.description.clone(),
th,
),
kv(
&i18n::t(app, "app.details.fields.architecture"),
d.architecture.clone(),
th,
),
kv(&i18n::t(app, "app.details.fields.url"), d.url.clone(), th),
kv(
&i18n::t(app, "app.details.fields.licences"),
join(&d.licenses),
th,
),
kv(
&i18n::t(app, "app.details.fields.provides"),
join(&d.provides),
th,
),
kv(
&i18n::t(app, "app.details.fields.depends_on"),
join(&d.depends),
th,
),
kv(
&i18n::t(app, "app.details.fields.optional_dependencies"),
join(&d.opt_depends),
th,
),
kv(
&i18n::t(app, "app.details.fields.required_by"),
join(&d.required_by),
th,
),
kv(
&i18n::t(app, "app.details.fields.optional_for"),
join(&d.optional_for),
th,
),
kv(
&i18n::t(app, "app.details.fields.conflicts_with"),
join(&d.conflicts),
th,
),
kv(
&i18n::t(app, "app.details.fields.replaces"),
join(&d.replaces),
th,
),
kv(
&i18n::t(app, "app.details.fields.download_size"),
d.download_size.map_or_else(
|| i18n::t(app, "app.details.fields.not_available"),
human_bytes,
),
th,
),
kv(
&i18n::t(app, "app.details.fields.install_size"),
d.install_size.map_or_else(
|| i18n::t(app, "app.details.fields.not_available"),
human_bytes,
),
th,
),
kv(
&i18n::t(app, "app.details.fields.package_owner"),
d.owner.clone(),
th,
),
kv(
&i18n::t(app, "app.details.fields.build_date"),
d.build_date.clone(),
th,
),
];
let pkgb_label = if app.pkgb_visible {
i18n::t(app, "app.details.hide_pkgbuild")
} else {
i18n::t(app, "app.details.show_pkgbuild")
};
lines.push(Line::from(vec![Span::styled(
pkgb_label,
Style::default()
.fg(th.mauve)
.add_modifier(Modifier::UNDERLINED | Modifier::BOLD),
)]));
if let Some(vote_state_text) = selected_aur_vote_state_text(app) {
lines.push(kv("AUR vote state", vote_state_text, th));
let comments_label = if app.comments_visible {
i18n::t(app, "app.details.hide_comments")
} else {
i18n::t(app, "app.details.show_comments")
};
lines.push(Line::from(vec![Span::styled(
comments_label,
Style::default()
.fg(th.mauve)
.add_modifier(Modifier::UNDERLINED | Modifier::BOLD),
)]));
}
lines
}
pub(crate) fn join(list: &[String]) -> String {
if list.is_empty() {
"-".into()
} else {
list.join(", ")
}
}
#[must_use]
pub fn format_bytes(value: u64) -> String {
const UNITS: [&str; 6] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
#[allow(clippy::cast_precision_loss)]
let mut size = value as f64;
let mut unit_index = 0usize;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
}
if unit_index == 0 {
format!("{value} {}", UNITS[unit_index])
} else {
format!("{size:.1} {}", UNITS[unit_index])
}
}
#[must_use]
pub fn format_signed_bytes(value: i64) -> String {
if value == 0 {
return "0 B".to_string();
}
let magnitude = value.unsigned_abs();
if value > 0 {
format!("+{}", format_bytes(magnitude))
} else {
format!("-{}", format_bytes(magnitude))
}
}
#[must_use]
pub fn human_bytes(n: u64) -> String {
const UNITS: [&str; 6] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
#[allow(clippy::cast_precision_loss)]
let mut v = n as f64;
let mut i = 0;
while v >= 1024.0 && i < UNITS.len() - 1 {
v /= 1024.0;
i += 1;
}
format!("{v:.1} {}", UNITS[i])
}