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}959697pub fn visible_width(text string) int {98 return strip_ansi(text).runes().len99}100101102103pub fn fit(text string, width int) string {104 if visible_width(text) <= width {105 return text106 }107 mut out := []rune{}108 mut shown := 0109 mut skipping := false110 for c in text.runes() {111 if c == `\x1b` {112 skipping = true113 }114 if skipping {115 out << c116 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) {117 skipping = false118 }119 continue120 }121 if shown == width {122 break123 }124 out << c125 shown++126 }127 cut := out.string()128 return if cut.contains('\x1b') { cut + '\x1b[0m' } else { cut }129}130131pub fn stamp(unix_seconds i64) string {132 return time.unix(unix_seconds).local().format()133}134135pub fn date_of(unix_seconds i64) string {136 return time.unix(unix_seconds).local().ymmdd()137}138139pub fn hour_of(unix_seconds i64) string {140 return time.unix(unix_seconds).local().hhmm()141}142143pub fn clock_of(unix_seconds i64) string {144 return time.unix(unix_seconds).local().hhmmss()145}146147pub fn human_bytes(bytes i64) string {148 mut value := f64(bytes)149 mut unit := 'B'150 for suffix in ['KiB', 'MiB', 'GiB'] {151 if value < 1024 {152 break153 }154 value /= 1024155 unit = suffix156 }157 if unit == 'B' {158 return '${bytes} B'159 }160 return '${value:.1f} ${unit}'161}162163164pub fn plural(count int, noun string) string {165 if count == 1 {166 return '1 ${noun}'167 }168 return '${count} ${noun}s'169}