//! Output formatting: the ✦ / ✗ habit, colour that can be turned off, and the //! `--json` shapes agents read. //! //! Every command that prints data has a JSON form with stable field names, and //! every error is `{"error": "..."}` on stderr. Exit codes are fixed: 0 //! success, 1 refusal or error, 2 bad usage. module cli import json import os const glyph_ok = '✦' const glyph_fail = '✗' pub fn color_enabled() bool { if os.getenv('NO_COLOR') != '' || os.getenv('KOH_NO_COLOR') != '' { return false } return os.is_atty(1) != 0 } fn paint(text string, code string) string { if !color_enabled() { return text } return '\x1b[${code}m${text}\x1b[0m' } pub fn amber(text string) string { return paint(text, '38;5;214') } pub fn red(text string) string { return paint(text, '38;5;203') } pub fn dim(text string) string { return paint(text, '38;5;245') } pub fn green(text string) string { return paint(text, '38;5;114') } pub fn ok(message string) { println(' ${amber(glyph_ok)} ${message}') } pub fn note(message string) { println(' ${dim(message)}') } pub fn warn(message string) { println(' ${amber('!')} ${message}') } // fail prints a refusal and returns the exit code to hand back. pub fn fail(message string) int { eprintln(' ${red(glyph_fail)} ${message}') return 1 } pub fn fail_json(message string) int { eprintln(json.encode({ 'error': message })) return 1 } pub fn usage_error(message string) int { eprintln(' ${red(glyph_fail)} ${message}') return 2 } // refuse reports an error in whichever shape the command was asked for. pub fn refuse(as_json bool, message string) int { if as_json { return fail_json(message) } return fail(message) } pub fn emit_json(value string) { println(value) }