use std::env;
use crate::localization::{self, keys};
use crate::output_mode::OutputMode;
use crate::theme::{self, ResolvedTheme, ThemeContext, ThemePreference};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutputPrefs {
resolved_theme: ResolvedTheme,
}
impl OutputPrefs {
#[must_use]
const fn from_theme(resolved_theme: ResolvedTheme) -> Self {
Self { resolved_theme }
}
#[must_use]
pub const fn emoji_allowed(self) -> bool {
self.resolved_theme.tokens.emoji_allowed
}
#[must_use]
pub const fn task_indent(self) -> &'static str {
self.resolved_theme.tokens.spacing.task_indent
}
#[must_use]
pub const fn timing_indent(self) -> &'static str {
self.resolved_theme.tokens.spacing.timing_indent
}
fn render_prefix(symbol: &'static str, label_key: &'static str) -> String {
let label = localization::message(label_key).to_string();
localization::message(keys::SEMANTIC_PREFIX_RENDERED)
.to_string()
.replace("{symbol}", symbol)
.replace("{label}", &label)
}
#[must_use]
pub fn error_prefix(self) -> String {
Self::render_prefix(
self.resolved_theme.tokens.symbols.error,
keys::SEMANTIC_PREFIX_ERROR,
)
}
#[must_use]
pub fn warning_prefix(self) -> String {
Self::render_prefix(
self.resolved_theme.tokens.symbols.warning,
keys::SEMANTIC_PREFIX_WARNING,
)
}
#[must_use]
pub fn success_prefix(self) -> String {
Self::render_prefix(
self.resolved_theme.tokens.symbols.success,
keys::SEMANTIC_PREFIX_SUCCESS,
)
}
#[must_use]
pub fn info_prefix(self) -> String {
Self::render_prefix(
self.resolved_theme.tokens.symbols.info,
keys::SEMANTIC_PREFIX_INFO,
)
}
#[must_use]
pub fn timing_prefix(self) -> String {
Self::render_prefix(
self.resolved_theme.tokens.symbols.timing,
keys::SEMANTIC_PREFIX_TIMING,
)
}
}
#[must_use]
#[expect(
clippy::disallowed_methods,
reason = "composition root: supplies the process environment to the read_env seam"
)]
pub fn resolve_from_theme(theme: Option<ThemePreference>, context: ThemeContext) -> OutputPrefs {
resolve_from_theme_with(theme, context, |key| env::var(key).ok())
}
#[must_use]
pub fn resolve_from_theme_with<F>(
theme: Option<ThemePreference>,
context: ThemeContext,
read_env: F,
) -> OutputPrefs
where
F: Fn(&str) -> Option<String>,
{
let resolved_theme = theme::resolve_theme(theme, context, read_env);
OutputPrefs::from_theme(resolved_theme)
}
#[must_use]
#[expect(
clippy::disallowed_methods,
reason = "composition root: supplies the process environment to the read_env seam"
)]
pub fn resolve(no_emoji: Option<bool>) -> OutputPrefs {
resolve_with(no_emoji, |key| env::var(key).ok())
}
#[must_use]
pub fn resolve_with<F>(no_emoji: Option<bool>, read_env: F) -> OutputPrefs
where
F: Fn(&str) -> Option<String>,
{
let resolved_theme = theme::resolve_theme(
None,
ThemeContext::new(no_emoji, None, OutputMode::Standard),
read_env,
);
OutputPrefs::from_theme(resolved_theme)
}
#[cfg(test)]
#[path = "output_prefs_tests.rs"]
mod tests;