use crate::theme::Theme;
use serde::Serialize;
use std::fmt::Write;
use tailspin::config::*;
use tailspin::style::Style;
pub fn default_theme_toml() -> String {
let Theme {
keywords: _,
regexes: _,
numbers,
uuids,
quotes,
ipv4,
ipv6,
dates,
durations,
paths,
urls,
emails,
pointers,
processes,
key_value_pairs,
json,
jvm_stack_traces,
} = Theme::default();
let mut out = String::from(
"\
# Generated by `tspin --generate-default-theme` — DO NOT EDIT.
#
# Every value below is a default: copying this file to
# ~/.config/tailspin/theme.toml and leaving it unchanged does nothing.
# Edit the styles you want to override and delete the rest.
#
# Keyword ([[keywords]]) and regex ([[regexes]]) highlights are additive
# lists and default to empty; the built-in keywords are compiled into tspin.
# See the README for their syntax.
",
);
push_config(&mut out, "numbers", &numbers);
push_config(&mut out, "uuids", &uuids);
push_quotes(&mut out, quotes);
push_config(&mut out, "ipv4", &ipv4);
push_config(&mut out, "ipv6", &ipv6);
push_config(&mut out, "dates", &dates);
push_config(&mut out, "durations", &durations);
push_config(&mut out, "paths", &paths);
push_config(&mut out, "urls", &urls);
push_config(&mut out, "emails", &emails);
push_config(&mut out, "pointers", &pointers);
push_config(&mut out, "processes", &processes);
push_config(&mut out, "key_value_pairs", &key_value_pairs);
push_config(&mut out, "json", &json);
push_config(&mut out, "jvm_stack_traces", &jvm_stack_traces);
out
}
fn push_config<T: Serialize>(out: &mut String, name: &str, config: &T) {
let table = toml::Value::try_from(config).expect("config structs serialize to tables");
let table = table.as_table().expect("config structs serialize to tables");
let styles: Vec<(&str, Style)> = table
.iter()
.map(|(field, style)| {
let style = style.clone().try_into().expect("config fields are styles");
(field.as_str(), style)
})
.collect();
push_table(out, name, &styles);
}
fn push_table(out: &mut String, name: &str, styles: &[(&str, Style)]) {
writeln!(out, "\n[{name}]").unwrap();
for (field, style) in styles {
writeln!(out, "{field} = {}", inline_style(*style)).unwrap();
}
}
fn push_quotes(out: &mut String, config: QuoteConfig) {
writeln!(out, "\n[quotes]\nquote_token = '{}'", config.quote_token as char).unwrap();
writeln!(out, "style = {}", inline_style(config.style)).unwrap();
}
fn inline_style(style: Style) -> String {
let table = toml::Value::try_from(style).expect("styles serialize to tables");
let table = table.as_table().expect("styles serialize to tables");
let fields: Vec<String> = table.iter().map(|(key, value)| format!("{key} = {value}")).collect();
format!("{{ {} }}", fields.join(", "))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generated_theme_parses_and_resolves_to_the_defaults() {
let generated = default_theme_toml();
let theme: Theme = toml::from_str(&generated).expect("generated theme must parse cleanly");
assert_eq!(theme.uuids.letter, UuidConfig::default().letter);
assert_eq!(theme.durations.unit, DurationConfig::default().unit);
assert_eq!(theme.numbers.style, NumberConfig::default().style);
assert_eq!(theme.quotes.quote_token, b'"');
assert_eq!(theme.ipv4.separator, IpV4Config::default().separator);
assert_eq!(theme.ipv6.letter, IpV6Config::default().letter);
}
}