1234567module fmt89import os10import time1112pub const glyph_ok = '✦'13pub const glyph_fail = '✗'1415pub fn colour_enabled() bool {16 if os.getenv('NO_COLOR') != '' || os.getenv('KOH_NO_COLOR') != '' {17 return false18 }19 return os.is_atty(1) != 020}2122fn paint(text string, code string) string {23 if text == '' || !colour_enabled() {24 return text25 }26 return '\x1b[${code}m${text}\x1b[0m'27}2829pub fn bold(text string) string {30 return paint(text, '1')31}3233pub fn reverse(text string) string {34 return paint(text, '7')35}3637pub fn amber(text string) string {38 return paint(text, '38;5;214')39}4041pub fn red(text string) string {42 return paint(text, '38;5;203')43}4445pub fn green(text string) string {46 return paint(text, '38;5;114')47}4849pub fn dim(text string) string {50 return paint(text, '38;5;245')51}52535455pub fn gold(text string) string {56 return paint(text, '38;5;179')57}5859pub fn muted(text string) string {60 return paint(text, '38;5;241')61}6263pub fn blue(text string) string {64 return paint(text, '38;5;110')65}666768pub fn light(text string) string {69 return paint(text, '38;5;223')70}7172pub fn warning(text string) string {73 return paint(text, '38;5;173')74}757677pub fn strip_ansi(text string) string {78 mut out := ''79 mut skipping := false80 for c in text.runes() {81 if c == `\x1b` {82 skipping = true83 continue84 }85 if skipping {86 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) {87 skipping = false88 }89 continue90 }91 out += c.str()92 }93 return out94}9596pub fn visible_width(text string) int {97 return strip_ansi(text).runes().len98}99100101102pub fn fit(text string, width int) string {103 if visible_width(text) <= width {104 return text105 }106 mut out := []rune{}107 mut shown := 0108 mut skipping := false109 for c in text.runes() {110 if c == `\x1b` {111 skipping = true112 }113 if skipping {114 out << c115 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) {116 skipping = false117 }118 continue119 }120 if shown == width {121 break122 }123 out << c124 shown++125 }126 cut := out.string()127 return if cut.contains('\x1b') { cut + '\x1b[0m' } else { cut }128}129130pub fn stamp(unix_seconds i64) string {131 return time.unix(unix_seconds).local().format()132}133134pub fn date_of(unix_seconds i64) string {135 return time.unix(unix_seconds).local().ymmdd()136}137138pub fn hour_of(unix_seconds i64) string {139 return time.unix(unix_seconds).local().hhmm()140}141142pub fn clock_of(unix_seconds i64) string {143 return time.unix(unix_seconds).local().hhmmss()144}145146pub fn human_bytes(bytes i64) string {147 mut value := f64(bytes)148 mut unit := 'B'149 for suffix in ['KiB', 'MiB', 'GiB'] {150 if value < 1024 {151 break152 }153 value /= 1024154 unit = suffix155 }156 if unit == 'B' {157 return '${bytes} B'158 }159 return '${value:.1f} ${unit}'160}161162163pub fn plural(count int, noun string) string {164 if count == 1 {165 return '1 ${noun}'166 }167 return '${count} ${noun}s'168}