///|
/// Render the fake system-info output for the given options.
pub fn render(opts : Options) -> String {
let model = opts.model.bind(model_by_name).unwrap_or(default_model())
let info = render_info(opts, model)
let logo = {
guard !opts.no_logo else { None }
opts.logo.map_or(Some(model.logo), name => logo_by_name(name))
}
match logo {
None => render_info_only(info)
Some(logo) =>
render_with_logo(info, decode_logo(logo, opts.color.unwrap_or("auto")))
}
}
///|
fn render_info(opts : Options, model : DeviceModel) -> Array[String] {
let user = opts.user.unwrap_or("kokic")
let host = opts.host.unwrap_or(model.host)
let header = "\{user}@\{host}"
let dashline = "-".repeat(header.char_length())
[
header,
dashline,
..field_line("OS", opts.os.unwrap_or(model.os)),
..field_line("Host", opts.device.unwrap_or(model.device)),
..field_line("Kernel", opts.kernel.unwrap_or(model.kernel)),
..field_line(
"Uptime",
structured_field(opts.uptime, model.uptime.render(), s => {
s.parse_uptime().map(u => u.render())
}),
),
..field_line("Shell", opts.shell.unwrap_or(model.shell)),
..field_line(
"Resolution",
structured_field(
opts.resolution,
model.resolution.map_or("", r => r.render()),
s => s.parse_resolution().map(r => r.render()),
),
),
..field_line("DE", opts.de.unwrap_or(model.de)),
..field_line("WM", opts.wm.unwrap_or(model.wm)),
..field_line("WM Theme", opts.wm_theme.unwrap_or(model.wm_theme)),
..field_line("Terminal", opts.terminal.unwrap_or(model.terminal)),
..field_line("Terminal Font", opts.term_font.unwrap_or(model.term_font)),
..field_line(
"CPU",
structured_field(opts.cpu, model.cpu.render(), s => {
s.parse_cpu().map(c => c.render())
}),
),
..field_line(
"GPU",
structured_field(opts.gpu, model.gpu.render(), s => {
s.parse_gpu().map(g => g.render())
}),
),
..field_line(
"Memory",
structured_field(opts.memory, model.memory.render(), s => {
s.parse_memory().map(m => m.render())
}),
),
]
}
///|
/// The rendered value of a structured field: an explicit override when it
/// parses, otherwise the model default. An empty override hides the line,
/// like other fields.
fn structured_field(
value : String?,
default : String,
parse : (String) -> String?,
) -> String {
match value {
Some("") => ""
Some(s) => parse(s).unwrap_or(default)
None => default
}
}
///|
fn ansi_code(color : String) -> String {
match color {
"black" => "30"
"red" => "31"
"green" => "32"
"yellow" => "33"
"blue" => "34"
"magenta" => "35"
"cyan" => "36"
"white" => "37"
_ => ""
}
}
///|
/// Render a number with two decimals, rounded to nearest hundredth,
/// e.g. `46.3` -> `"46.30"`.
fn fixed_two(x : Float) -> String {
let hundredths = (x * 100.0).round().to_int()
let whole = hundredths / 100
let frac = hundredths % 100
let frac_text = if frac < 10 { "0\{frac}" } else { "\{frac}" }
"\{whole}.\{frac_text}"
}
///|
fn render_info_only(info : Array[String]) -> String {
"\{info.join("\n")}\n"
}
///|
fn render_with_logo(info : Array[String], rendered : DecodedLogo) -> String {
let buf = StringBuilder()
let width = rendered.max_width
let rows = rendered.lines.length().max(info.length())
for i in 0.. {
buf.write_string(text)
buf.write_string(" ".repeat(width - line_width))
}
None => buf.write_string(" ".repeat(width))
}
buf.write_string(" ")
if info.get(i) is Some(text) {
buf.write_string(text)
}
buf.write_string("\n")
}
buf.to_string()
}
///|
fn field_line(label : String, value : String) -> Array[String] {
guard !value.is_empty() else { [] }
["\{label}: \{value}"]
}
///|
priv struct DecodedLogo {
lines : Array[(String, Int)]
max_width : Int
}
///|
/// The palette entry used to color a logo cell: the forced color wins,
/// otherwise the palette color at `index`, unless colors are disabled.
fn resolve_code(
palette : Array[String],
forced_code : String,
no_color : Bool,
index : Int,
) -> String {
guard !no_color else { "" }
if !forced_code.is_empty() {
forced_code
} else {
palette.get(index).unwrap_or("")
}
}
///|
fn decode_logo(logo : Logo, color_mode : String) -> DecodedLogo {
let no_color = color_mode == "none"
let forced_code = match color_mode {
"auto" | "none" => ""
_ => ansi_code(color_mode)
}
let mut carry = resolve_code(logo.palette, forced_code, no_color, 0)
let lines : Array[(String, Int)] = []
let mut max_width = 0
for line in logo.lines {
let decoded = decode_logo_line(
line,
logo.palette,
forced_code,
carry,
no_color,
)
carry = decoded.carry
lines.push((decoded.text, decoded.width))
max_width = max_width.max(decoded.width)
}
{ lines, max_width }
}
///|
priv struct DecodedLogoLine {
text : String
width : Int
carry : String
}
///|
fn write_ansi(buf : StringBuilder, code : String) -> Unit {
buf.write_string("\u{1B}[")
buf.write_string(code)
buf.write_string("m")
}
///|
fn decode_logo_line(
line : String,
palette : Array[String],
forced_code : String,
start_carry : String,
no_color : Bool,
) -> DecodedLogoLine {
let buf = StringBuilder()
let mut carry = start_carry
let mut width = 0
if !carry.is_empty() {
write_ansi(buf, carry)
}
let chars = line.to_array()
let mut index = 0
while index < chars.length() {
match (chars[index], chars.get(index + 1)) {
('$', Some('$')) => {
buf.write_char('$')
width += 1
index += 2
}
('$', Some(n)) if n >= '1' && n <= '9' => {
let code = resolve_code(
palette,
forced_code,
no_color,
n.to_int() - '1'.to_int(),
)
carry = code
if !code.is_empty() {
write_ansi(buf, code)
}
index += 2
}
('$', _) => {
buf.write_char('$')
width += 1
index += 1
}
(c, _) => {
buf.write_char(c)
width += 1
index += 1
}
}
}
if !carry.is_empty() {
buf.write_string("\u{1B}[0m")
}
{ text: buf.to_string(), width, carry }
}