3 Commits
v1.0.4 ... main

Author SHA1 Message Date
Darwin Cereska
cfa7d10048 Create README.md 2025-11-24 12:27:05 -05:00
darwincereska
8d05aa4ff5 fix(bugs): bugfix 2025-11-24 10:36:57 -05:00
darwincereska
c6470be0df feat(colors): added more colors and --no-color flag 2025-11-24 10:22:43 -05:00
8 changed files with 454 additions and 55 deletions

45
README.md Normal file
View File

@@ -0,0 +1,45 @@
# kargs
All-in-one tool for building cli applications in Kotlin
# Installation
```kotlin
/** build.gradle.kts */
repositories {
mavenCentral()
}
dependencies {
implementation("org.kargs:kargs:version")
}
```
# Usage
## Parser
```kotlin
// Main.kt
import org.kargs.*
fun main(args: Array<String>) {
val parser = Parser("program name")
// Register subcommands
parser.subcommands(
TestCommand1(),
TestCommand2(),
...
)
parser.parse(args)
```
## Subcommand
```kotlin
// Subcommand.kt
import org.kargs.*
class TestCommand : Subcommand("name", "description", aliases = listOf("alias1", "alias2")) {
override fun execute() {
println("Logic")
}
}

View File

@@ -4,7 +4,7 @@ plugins {
} }
group = "org.kargs" group = "org.kargs"
version = "1.0.4" version = "1.0.8"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -80,7 +80,7 @@ sealed class ArgType<T>(val typeName: String) {
/** /**
* Optional value type - can be used as flag or with value * Optional value type - can be used as flag or with value
*/ */
class OptionalValue(val defaultWhenPresent: String = "true") : ArgType<String>("OptionalValue") { class OptionalValue(val defaultWhenPresent: String = "true") : ArgType<kotlin.String>("OptionalValue") {
override fun convert(value: String): String = value override fun convert(value: String): String = value
override fun getValidationDescription(): String = "optional value or flag" override fun getValidationDescription(): String = "optional value or flag"
@@ -95,7 +95,7 @@ sealed class ArgType<T>(val typeName: String) {
val mustBeDirectory: Boolean = false, val mustBeDirectory: Boolean = false,
val mustBeReadable: Boolean = false, val mustBeReadable: Boolean = false,
val mustBeWritable: Boolean = false val mustBeWritable: Boolean = false
) : ArgType<File>("File") { ) : ArgType<java.io.File>("File") {
override fun convert(value: String): File = File(value) override fun convert(value: String): File = File(value)
override fun validate(value: File): Boolean { override fun validate(value: File): Boolean {

View File

@@ -0,0 +1,342 @@
package org.kargs
object Colors {
enum class Color(val code: String) {
RESET("\u001B[0m"),
// Base colors
BLACK("\u001B[30m"),
RED("\u001B[31m"),
GREEN("\u001B[32m"),
YELLOW("\u001B[33m"),
BLUE("\u001B[34m"),
MAGENTA("\u001B[35m"),
CYAN("\u001B[36m"),
WHITE("\u001B[37m"),
GRAY("\u001B[90m"),
// Bright colors
BRIGHT_BLACK("\u001B[90m"),
BRIGHT_RED("\u001B[91m"),
BRIGHT_GREEN("\u001B[92m"),
BRIGHT_YELLOW("\u001B[93m"),
BRIGHT_BLUE("\u001B[94m"),
BRIGHT_MAGENTA("\u001B[95m"),
BRIGHT_CYAN("\u001B[96m"),
BRIGHT_WHITE("\u001B[97m"),
// Extended 256-color palette (some popular ones)
ORANGE("\u001B[38;5;208m"),
PURPLE("\u001B[38;5;129m"),
PINK("\u001B[38;5;205m"),
LIME("\u001B[38;5;154m"),
TEAL("\u001B[38;5;80m"),
NAVY("\u001B[38;5;17m"),
MAROON("\u001B[38;5;88m"),
OLIVE("\u001B[38;5;100m"),
SILVER("\u001B[38;5;248m"),
GOLD("\u001B[38;5;220m"),
CORAL("\u001B[38;5;209m"),
SALMON("\u001B[38;5;210m"),
KHAKI("\u001B[38;5;185m"),
VIOLET("\u001B[38;5;177m"),
INDIGO("\u001B[38;5;54m"),
TURQUOISE("\u001B[38;5;80m"),
CRIMSON("\u001B[38;5;196m"),
FOREST_GREEN("\u001B[38;5;22m"),
ROYAL_BLUE("\u001B[38;5;21m"),
DARK_ORANGE("\u001B[38;5;166m"),
LIGHT_GRAY("\u001B[38;5;250m"),
DARK_GRAY("\u001B[38;5;240m"),
// Pastel colors
PASTEL_PINK("\u001B[38;5;217m"),
PASTEL_BLUE("\u001B[38;5;153m"),
PASTEL_GREEN("\u001B[38;5;157m"),
PASTEL_YELLOW("\u001B[38;5;229m"),
PASTEL_PURPLE("\u001B[38;5;183m"),
PASTEL_ORANGE("\u001B[38;5;223m"),
// Styles
BOLD("\u001B[1m"),
DIM("\u001B[2m"),
ITALIC("\u001B[3m"),
UNDERLINE("\u001B[4m"),
BLINK("\u001B[5m"),
REVERSE("\u001B[7m"),
STRIKETHROUGH("\u001B[9m"),
// Background colors
BG_BLACK("\u001B[40m"),
BG_RED("\u001B[41m"),
BG_GREEN("\u001B[42m"),
BG_YELLOW("\u001B[43m"),
BG_BLUE("\u001B[44m"),
BG_MAGENTA("\u001B[45m"),
BG_CYAN("\u001B[46m"),
BG_WHITE("\u001B[47m"),
// Semantic presets
ERROR("${BRIGHT_RED.code}${BOLD.code}"),
WARN("${YELLOW.code}${BOLD.code}"),
INFO(CYAN.code),
DEBUG("${DIM.code}${GRAY.code}"),
SUCCESS("${BRIGHT_GREEN.code}${BOLD.code}"),
// File/UI semantic colors
FILENAME("${BRIGHT_BLUE.code}${BOLD.code}"),
HEADING("${BRIGHT_MAGENTA.code}${BOLD.code}"),
SUBHEADING("${MAGENTA.code}${BOLD.code}"),
EMPHASIS("${BRIGHT_YELLOW.code}${BOLD.code}"),
HIGHLIGHT("${BG_YELLOW.code}${BLACK.code}"),
LINK("${BRIGHT_CYAN.code}${UNDERLINE.code}"),
CODE(BRIGHT_GREEN.code),
COMMENT("${DIM.code}${GRAY.code}"),
KEYWORD(BRIGHT_MAGENTA.code),
STRING(GREEN.code),
NUMBER(BRIGHT_BLUE.code),
OPERATOR(BRIGHT_RED.code),
// Status colors
ACTIVE(BRIGHT_GREEN.code),
INACTIVE("${DIM.code}${GRAY.code}"),
PENDING(YELLOW.code),
FAILED(BRIGHT_RED.code),
PASSED(BRIGHT_GREEN.code),
SKIPPED(BRIGHT_YELLOW.code),
// UI Elements
BORDER(DARK_GRAY.code),
MENU_ITEM(BRIGHT_WHITE.code),
SELECTED("${BG_BLUE.code}${BRIGHT_WHITE.code}"),
DISABLED("${DIM.code}${GRAY.code}"),
// Log levels
TRACE("${DIM.code}${LIGHT_GRAY.code}"),
VERBOSE(GRAY.code),
FATAL("${BG_RED.code}${BRIGHT_WHITE.code}${BOLD.code}");
fun bold() = DerivedColor("${BOLD.code}$code")
fun dim() = DerivedColor("${DIM.code}$code")
fun italic() = DerivedColor("${ITALIC.code}$code")
fun underline() = DerivedColor("${UNDERLINE.code}$code")
fun strikethrough() = DerivedColor("${STRIKETHROUGH.code}$code")
fun reverse() = DerivedColor("${REVERSE.code}$code")
fun blink() = DerivedColor("${BLINK.code}$code")
// Combine with background
fun onBackground(bgColor: Color) = DerivedColor("${bgColor.code}$code")
}
/** Wrapper for modified colors */
class DerivedColor(val combinedCode: String) {
fun bold() = DerivedColor("${Color.BOLD.code}$combinedCode")
fun dim() = DerivedColor("${Color.DIM.code}$combinedCode")
fun italic() = DerivedColor("${Color.ITALIC.code}$combinedCode")
fun underline() = DerivedColor("${Color.UNDERLINE.code}$combinedCode")
fun strikethrough() = DerivedColor("${Color.STRIKETHROUGH.code}$combinedCode")
fun reverse() = DerivedColor("${Color.REVERSE.code}$combinedCode")
fun blink() = DerivedColor("${Color.BLINK.code}$combinedCode")
}
// Global enable/disable
private var globalColorsEnabled = true
fun setGlobalColorsEnabled(enabled: Boolean) { globalColorsEnabled = enabled }
fun areGlobalColorsEnabled(): Boolean = globalColorsEnabled
// colorize() overloads
fun colorize(text: String, color: Color): String =
if (globalColorsEnabled) "${color.code}$text${Color.RESET.code}" else text
fun colorize(text: String, color: DerivedColor): String =
if (globalColorsEnabled) "${color.combinedCode}$text${Color.RESET.code}" else text
// RGB color support (for terminals that support it)
fun rgb(r: Int, g: Int, b: Int): DerivedColor =
DerivedColor("\u001B[38;2;$r;$g;${b}m")
fun bgRgb(r: Int, g: Int, b: Int): DerivedColor =
DerivedColor("\u001B[48;2;$r;$g;${b}m")
// Hex color support
fun hex(hexColor: String): DerivedColor {
val hex = hexColor.removePrefix("#")
val r = hex.take(2).toInt(16)
val g = hex.substring(2, 4).toInt(16)
val b = hex.substring(4, 6).toInt(16)
return rgb(r, g, b)
}
// --------------------------------------------------------
// Semantic convenience helpers
// --------------------------------------------------------
fun filename(text: String) = colorize(text, Color.FILENAME)
fun heading(text: String) = colorize(text, Color.HEADING)
fun subheading(text: String) = colorize(text, Color.SUBHEADING)
fun emphasis(text: String) = colorize(text, Color.EMPHASIS)
fun highlight(text: String) = colorize(text, Color.HIGHLIGHT)
fun link(text: String) = colorize(text, Color.LINK)
fun code(text: String) = colorize(text, Color.CODE)
fun comment(text: String) = colorize(text, Color.COMMENT)
fun keyword(text: String) = colorize(text, Color.KEYWORD)
fun string(text: String) = colorize(text, Color.STRING)
fun number(text: String) = colorize(text, Color.NUMBER)
fun operator(text: String) = colorize(text, Color.OPERATOR)
// Status helpers
fun active(text: String) = colorize(text, Color.ACTIVE)
fun inactive(text: String) = colorize(text, Color.INACTIVE)
fun pending(text: String) = colorize(text, Color.PENDING)
fun failed(text: String) = colorize(text, Color.FAILED)
fun passed(text: String) = colorize(text, Color.PASSED)
fun skipped(text: String) = colorize(text, Color.SKIPPED)
fun success(text: String) = colorize(text, Color.SUCCESS)
// Log level helpers
fun trace(text: String) = colorize(text, Color.TRACE)
fun verbose(text: String) = colorize(text, Color.VERBOSE)
fun fatal(text: String) = colorize(text, Color.FATAL)
// --------------------------------------------------------
// Extended color convenience helpers
// --------------------------------------------------------
fun orange(text: String) = colorize(text, Color.ORANGE)
fun purple(text: String) = colorize(text, Color.PURPLE)
fun pink(text: String) = colorize(text, Color.PINK)
fun lime(text: String) = colorize(text, Color.LIME)
fun teal(text: String) = colorize(text, Color.TEAL)
fun navy(text: String) = colorize(text, Color.NAVY)
fun maroon(text: String) = colorize(text, Color.MAROON)
fun olive(text: String) = colorize(text, Color.OLIVE)
fun silver(text: String) = colorize(text, Color.SILVER)
fun gold(text: String) = colorize(text, Color.GOLD)
fun coral(text: String) = colorize(text, Color.CORAL)
fun salmon(text: String) = colorize(text, Color.SALMON)
fun khaki(text: String) = colorize(text, Color.KHAKI)
fun violet(text: String) = colorize(text, Color.VIOLET)
fun indigo(text: String) = colorize(text, Color.INDIGO)
fun turquoise(text: String) = colorize(text, Color.TURQUOISE)
fun crimson(text: String) = colorize(text, Color.CRIMSON)
fun forestGreen(text: String) = colorize(text, Color.FOREST_GREEN)
fun royalBlue(text: String) = colorize(text, Color.ROYAL_BLUE)
fun darkOrange(text: String) = colorize(text, Color.DARK_ORANGE)
fun lightGray(text: String) = colorize(text, Color.LIGHT_GRAY)
fun darkGray(text: String) = colorize(text, Color.DARK_GRAY)
// Pastel helpers
fun pastelPink(text: String) = colorize(text, Color.PASTEL_PINK)
fun pastelBlue(text: String) = colorize(text, Color.PASTEL_BLUE)
fun pastelGreen(text: String) = colorize(text, Color.PASTEL_GREEN)
fun pastelYellow(text: String) = colorize(text, Color.PASTEL_YELLOW)
fun pastelPurple(text: String) = colorize(text, Color.PASTEL_PURPLE)
fun pastelOrange(text: String) = colorize(text, Color.PASTEL_ORANGE)
// --------------------------------------------------------
// Original convenience helpers (keeping all existing ones)
// --------------------------------------------------------
fun bold(text: String) = colorize(text, Color.WHITE.bold())
fun dim(text: String) = colorize(text, Color.GRAY)
fun black(text: String) = colorize(text, Color.BLACK)
fun red(text: String) = colorize(text, Color.RED)
fun green(text: String) = colorize(text, Color.GREEN)
fun yellow(text: String) = colorize(text, Color.YELLOW)
fun blue(text: String) = colorize(text, Color.BLUE)
fun magenta(text: String) = colorize(text, Color.MAGENTA)
fun cyan(text: String) = colorize(text, Color.CYAN)
fun white(text: String) = colorize(text, Color.WHITE)
fun gray(text: String) = colorize(text, Color.GRAY)
fun brightBlack(text: String) = colorize(text, Color.BRIGHT_BLACK)
fun brightRed(text: String) = colorize(text, Color.BRIGHT_RED)
fun brightGreen(text: String) = colorize(text, Color.BRIGHT_GREEN)
fun brightYellow(text: String) = colorize(text, Color.BRIGHT_YELLOW)
fun brightBlue(text: String) = colorize(text, Color.BRIGHT_BLUE)
fun brightMagenta(text: String) = colorize(text, Color.BRIGHT_MAGENTA)
fun brightCyan(text: String) = colorize(text, Color.BRIGHT_CYAN)
fun brightWhite(text: String) = colorize(text, Color.BRIGHT_WHITE)
// Presets
fun error(text: String) = colorize(text, Color.ERROR)
fun warn(text: String) = colorize(text, Color.WARN)
fun info(text: String) = colorize(text, Color.INFO)
fun debug(text: String) = colorize(text, Color.DEBUG)
// Bold helpers (keeping all existing ones)
fun boldBlack(text: String) = colorize(text, Color.BLACK.bold())
fun boldRed(text: String) = colorize(text, Color.RED.bold())
fun boldGreen(text: String) = colorize(text, Color.GREEN.bold())
fun boldYellow(text: String) = colorize(text, Color.YELLOW.bold())
fun boldBlue(text: String) = colorize(text, Color.BLUE.bold())
fun boldMagenta(text: String) = colorize(text, Color.MAGENTA.bold())
fun boldCyan(text: String) = colorize(text, Color.CYAN.bold())
fun boldWhite(text: String) = colorize(text, Color.WHITE.bold())
fun boldBrightBlack(text: String) = colorize(text, Color.BRIGHT_BLACK.bold())
fun boldBrightRed(text: String) = colorize(text, Color.BRIGHT_RED.bold())
fun boldBrightGreen(text: String) = colorize(text, Color.BRIGHT_GREEN.bold())
fun boldBrightYellow(text: String) = colorize(text, Color.BRIGHT_YELLOW.bold())
fun boldBrightBlue(text: String) = colorize(text, Color.BRIGHT_BLUE.bold())
fun boldBrightMagenta(text: String) = colorize(text, Color.BRIGHT_MAGENTA.bold())
fun boldBrightCyan(text: String) = colorize(text, Color.BRIGHT_CYAN.bold())
fun boldBrightWhite(text: String) = colorize(text, Color.BRIGHT_WHITE.bold())
// Dim helpers (keeping all existing ones)
fun dimBlack(text: String) = colorize(text, Color.BLACK.dim())
fun dimRed(text: String) = colorize(text, Color.RED.dim())
fun dimGreen(text: String) = colorize(text, Color.GREEN.dim())
fun dimYellow(text: String) = colorize(text, Color.YELLOW.dim())
fun dimBlue(text: String) = colorize(text, Color.BLUE.dim())
fun dimMagenta(text: String) = colorize(text, Color.MAGENTA.dim())
fun dimCyan(text: String) = colorize(text, Color.CYAN.dim())
fun dimWhite(text: String) = colorize(text, Color.WHITE.dim())
fun dimBrightBlack(text: String) = colorize(text, Color.BRIGHT_BLACK.dim())
fun dimBrightRed(text: String) = colorize(text, Color.BRIGHT_RED.dim())
fun dimBrightGreen(text: String) = colorize(text, Color.BRIGHT_GREEN.dim())
fun dimBrightYellow(text: String) = colorize(text, Color.BRIGHT_YELLOW.dim())
fun dimBrightBlue(text: String) = colorize(text, Color.BRIGHT_BLUE.dim())
fun dimBrightMagenta(text: String) = colorize(text, Color.BRIGHT_MAGENTA.dim())
fun dimBrightCyan(text: String) = colorize(text, Color.BRIGHT_CYAN.dim())
fun dimBrightWhite(text: String) = colorize(text, Color.BRIGHT_WHITE.dim())
// --------------------------------------------------------
// Utility functions
// --------------------------------------------------------
// Create a gradient effect
fun gradient(text: String, startColor: Color, endColor: Color): String {
if (!globalColorsEnabled || text.isEmpty()) return text
val length = text.length
if (length == 1) return colorize(text, startColor)
return text.mapIndexed { index, char ->
val ratio = index.toFloat() / (length - 1)
// Simple interpolation between colors (this is a basic implementation)
val color = if (ratio < 0.5f) startColor else endColor
colorize(char.toString(), color)
}.joinToString("")
}
// Rainbow effect
fun rainbow(text: String): String {
if (!globalColorsEnabled) return text
val colors = listOf(
Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN,
Color.CYAN, Color.BLUE, Color.MAGENTA
)
return text.mapIndexed { index, char ->
val colorIndex = index % colors.size
colorize(char.toString(), colors[colorIndex])
}.joinToString("")
}
// Strip all ANSI codes
fun stripColors(text: String): String = text.replace(Regex("\u001B\\[[0-9;]*m"), "")
// Get length without ANSI codes
fun getDisplayLength(text: String): Int = stripColors(text).length
}

View File

@@ -48,13 +48,18 @@ class Parser(
} }
// Check for help, global or command // Check for help, global or command
if (args.contains("--help") || args.contains("-h")) { if ("--help" in args || "-h" in args) {
cmd.printHelp() cmd.printHelp()
return return
} }
// Check for no-color
if ("--no-color" in args) {
Colors.setGlobalColorsEnabled(false)
}
try { try {
parseCommandArgs(cmd, args.sliceArray(1 until args.size)) parseCommandArgs(cmd, args.sliceArray(1 ..< args.size))
validateRequiredOptions(cmd) validateRequiredOptions(cmd)
cmd.execute() cmd.execute()
} catch (e: ArgumentParseException) { } catch (e: ArgumentParseException) {
@@ -71,7 +76,7 @@ class Parser(
return commands.firstOrNull { cmd -> return commands.firstOrNull { cmd ->
val cmdName = if (config.caseSensitive) cmd.name else cmd.name.lowercase() val cmdName = if (config.caseSensitive) cmd.name else cmd.name.lowercase()
val aliases = if (config.caseSensitive) cmd.aliases else cmd.aliases.map { it.lowercase() } val aliases = if (config.caseSensitive) cmd.aliases else cmd.aliases.map { it.lowercase() }
cmdName == searchName || aliases.contains(searchName) cmdName == searchName || searchName in aliases
} }
} }
@@ -85,6 +90,9 @@ class Parser(
while (i < args.size) { while (i < args.size) {
val arg = args[i] val arg = args[i]
if ("--help" in arg || "-h" in arg) i++
if ("--no-color" in arg) i++
when { when {
arg.startsWith("--") -> { arg.startsWith("--") -> {
val key = arg.removePrefix("--") val key = arg.removePrefix("--")
@@ -163,10 +171,13 @@ class Parser(
if (key.length > 1) { if (key.length > 1) {
key.forEach { char -> key.forEach { char ->
val flag = cmd.flags.firstOrNull { it.shortName == char.toString() } val flag = cmd.flags.firstOrNull { it.shortName == char.toString() }
if (flag != null) { val optionalOption = cmd.optionalOptions.firstOrNull { it.shortName == char.toString() }
flag.setFlag()
} else if (config.strictMode) { when {
throw ArgumentParseException("Unknown flag -$char") flag != null -> flag.setFlag()
optionalOption != null -> optionalOption.setAsFlag()
config.strictMode -> throw ArgumentParseException("Unknown flag -$char")
else -> printWarning("Unknown flag -$char")
} }
} }
return index return index
@@ -174,6 +185,7 @@ class Parser(
val option = cmd.options.firstOrNull { it.shortName == key } val option = cmd.options.firstOrNull { it.shortName == key }
val flag = cmd.flags.firstOrNull { it.shortName == key } val flag = cmd.flags.firstOrNull { it.shortName == key }
val optionalOption = cmd.optionalOptions.firstOrNull { it.shortName == key }
return when { return when {
option != null -> { option != null -> {
@@ -188,6 +200,19 @@ class Parser(
} }
} }
optionalOption != null -> {
// Check if next arg exists and doesn't start with -
if (index + 1 < args.size && !args[index + 1].startsWith("-")) {
// Has value
optionalOption.parseValue(args[index + 1])
index + 1
} else {
// Used as flag
optionalOption.setAsFlag()
index
}
}
flag != null -> { flag != null -> {
flag.setFlag() flag.setFlag()
index index
@@ -258,16 +283,17 @@ class Parser(
* Print global help menu * Print global help menu
*/ */
private fun printGlobalHelp() { private fun printGlobalHelp() {
val versionInfo = config.programVersion?.let { " (v$it)" } ?: "" val versionInfo = config.programVersion?.let { " ${Colors.dimBlue("(v$it)")}" } ?: ""
println(colorize("Usage: $programName$versionInfo <command> [options]", Color.BOLD)) println(Colors.boldWhite("Usage: $programName$versionInfo <command> [options]"))
println() println()
println(colorize("Commands:", Color.BOLD)) println(Colors.boldWhite("Commands:"))
commands.forEach { cmd -> commands.forEach { cmd ->
val aliases = if (cmd.aliases.isNotEmpty()) " (${cmd.aliases.joinToString(", ")})" else "" val aliases = if (cmd.aliases.isNotEmpty()) " (${cmd.aliases.joinToString(", ")})" else ""
println(" ${colorize(cmd.name, Color.GREEN)}$aliases") println(" ${cmd.name}${Colors.dimBlue(aliases)}")
if (cmd.description.isNotEmpty()) { if (cmd.description.isNotEmpty()) {
println(" ${cmd.description}") println(Colors.dimMagenta(" ${cmd.description}"))
} }
println()
} }
println() println()
println("Use `$programName <command> --help` for more information about a command.") println("Use `$programName <command> --help` for more information about a command.")
@@ -277,36 +303,14 @@ class Parser(
* Print error message with optional coloring * Print error message with optional coloring
*/ */
private fun printError(message: String) { private fun printError(message: String) {
println(colorize("Error: $message", Color.RED)) println(Colors.error("Error: $message"))
} }
/** /**
* Print warning message with optional coloring * Print warning message with optional coloring
*/ */
private fun printWarning(message: String) { private fun printWarning(message: String) {
println(colorize("Warning: $message", Color.YELLOW)) println(Colors.warn("Warning: $message"))
}
/**
* Apply color to text if colors are enabled
*/
private fun colorize(text: String, color: Color): String {
return if (config.colorsEnabled) {
"${color.code}$text${Color.RESET.code}"
} else {
text
}
}
/**
* ANSI color codes for terminal output
*/
private enum class Color(val code: String) {
RESET("\u001B[0m"),
RED("\u001B[31m"),
GREEN("\u001B[32m"),
YELLOW("\u001B[33m"),
BOLD("\u001B[1m")
} }
/** /**

View File

@@ -18,4 +18,8 @@ data class ParserConfig(
companion object { companion object {
val DEFAULT = ParserConfig() val DEFAULT = ParserConfig()
} }
init {
Colors.setGlobalColorsEnabled(colorsEnabled)
}
} }

View File

@@ -50,63 +50,67 @@ abstract class Subcommand(
* Print help information for this subcommand * Print help information for this subcommand
*/ */
fun printHelp() { fun printHelp() {
println("Usage: $name [options]${if (arguments.isNotEmpty()) " ${arguments.joinToString(" ") { if (it.required) "<${it.name}>" else "[${it.name}]" }}" else ""}") println(Colors.boldWhite("Usage: $name ${"[options]"}${if (arguments.isNotEmpty()) " ${arguments.joinToString(" ") { if (it.required) "<${it.name}>" else "[${it.name}]" }}" else ""}"))
if (description.isNotEmpty()) { if (description.isNotEmpty()) {
println() println()
println(description) println(Colors.boldMagenta(description))
} }
if (options.isNotEmpty()) { if (options.isNotEmpty()) {
println() println()
println("Options:") println(Colors.boldWhite("Options:"))
options.forEach { option -> options.forEach { option ->
val shortName = option.shortName?.let { "-$it, " } ?: " " val shortName = option.shortName?.let { "-$it, " } ?: " "
val required = if (option.required) " (required)" else "" val required = if (option.required) Colors.dimBlue(" (required)") else ""
val defaultVal = option.getValueOrDefault()?.let { " [default: $it]" } ?: "" val defaultVal = option.getValueOrDefault()?.let { Colors.boldBlue(" [default: $it]") } ?: ""
val typeInfo = getTypeInfo(option.type) val typeInfo = Colors.yellow(getTypeInfo(option.type))
println(" $shortName--${option.longName}${typeInfo}") println(" $shortName--${option.longName}${typeInfo}")
option.description?.let { desc -> option.description?.let { desc ->
println(" $desc$required$defaultVal") println(Colors.dimMagenta(" $desc$required$defaultVal"))
} }
println()
} }
} }
if (optionalOptions.isNotEmpty()) { if (optionalOptions.isNotEmpty()) {
println() println()
println("Optional Value Options:") println(Colors.boldWhite("Optional Value Options:"))
optionalOptions.forEach { option -> optionalOptions.forEach { option ->
val shortName = option.shortName?.let { "-$it, " } ?: " " val shortName = option.shortName?.let { "-$it, " } ?: " "
println(" $shortName--${option.longName} [value]") println(" $shortName--${option.longName} [value]")
option.description?.let { desc -> option.description?.let { desc ->
println(" $desc (can be used as flag or with value)") println(Colors.dimMagenta(" $desc (can be used as flag or with value)"))
} }
println()
} }
} }
if (flags.isNotEmpty()) { if (flags.isNotEmpty()) {
println() println()
println("Flags:") println(Colors.boldWhite("Flags:"))
flags.forEach { flag -> flags.forEach { flag ->
val shortName = flag.shortName?.let { "-$it, " } ?: " " val shortName = flag.shortName?.let { "-$it, " } ?: " "
println(" $shortName--${flag.longName}") println(" $shortName--${flag.longName}")
flag.description?.let { desc -> flag.description?.let { desc ->
println(" $desc") println(Colors.dimMagenta(" $desc"))
} }
println()
} }
} }
if (arguments.isNotEmpty()) { if (arguments.isNotEmpty()) {
println() println()
println("Arguments:") println(Colors.boldWhite("Arguments:"))
arguments.forEach { arg -> arguments.forEach { arg ->
val required = if (arg.required) " (required)" else " (optional)" val required = Colors.dimBlue(if (arg.required) " (required)" else " (optional)")
val typeInfo = getTypeInfo(arg.type) val typeInfo = Colors.yellow(getTypeInfo(arg.type))
println(" ${arg.name}$typeInfo$required") println(" ${arg.name}$typeInfo$required")
arg.description?.let { desc -> arg.description?.let { desc ->
println(" $desc") println(Colors.dimMagenta(" $desc"))
} }
println()
} }
} }
} }