6 Commits
0.3.4 ... 0.6.1

Author SHA1 Message Date
9750ac16b3 Add version of queryParamsToCliArgs that accepts a Table[string,string] to support Jester 0.4 2019-01-17 13:19:59 -06:00
7b274bfb98 Update to support Nim 0.19 (removal of string nil specifically). 2018-12-09 03:23:12 -06:00
e0eb8fd4c2 Add functions for a simple CLI<->HTTP layer. 2018-10-02 12:09:18 -05:00
d368e85e33 Refactor color functions to provide a unified interface for fore/back ground. 2018-05-29 14:18:14 -05:00
49342818ec Add basic support for ANSI color escape codes.
Nim's `terminal` library provides ANSI terminal escape support, but
only when writing directly to a File handle. This commit adds the
`term{FG,BG}` procedures and `termReset` constant to help with embedding
ANSI escape codes in string data.
2018-05-29 12:53:42 -05:00
95f3bc48bd Make the CombinedConfig.getVal function more tolerant of non-string values. 2018-05-18 11:18:40 -05:00
2 changed files with 66 additions and 19 deletions

View File

@ -1,4 +1,4 @@
import docopt, json, osproc, posix, nre, streams, strtabs, unicode import docopt, json, osproc, posix, nre, streams, strtabs, tables, terminal, unicode
import os except sleep import os except sleep
import strutils except toUpper, toLower import strutils except toUpper, toLower
@ -8,6 +8,8 @@ type
docopt*: Table[string, Value] docopt*: Table[string, Value]
json*: JsonNode json*: JsonNode
TermColor = ForegroundColor or BackgroundColor
proc getVal*(cfg: CombinedConfig, key, default: string): string = proc getVal*(cfg: CombinedConfig, key, default: string): string =
let argKey = "--" & key let argKey = "--" & key
let envKey = key.replace('-', '_').toUpper let envKey = key.replace('-', '_').toUpper
@ -15,7 +17,16 @@ proc getVal*(cfg: CombinedConfig, key, default: string): string =
if cfg.docopt[argKey]: return $cfg.docopt[argKey] if cfg.docopt[argKey]: return $cfg.docopt[argKey]
elif existsEnv(envKey): return getEnv(envKey) elif existsEnv(envKey): return getEnv(envKey)
elif cfg.json.hasKey(jsonKey): return cfg.json[jsonKey].getStr elif cfg.json.hasKey(jsonKey):
let node = cfg.json[jsonKey]
case node.kind
of JString: return node.getStr
of JInt: return $node.getInt
of JFloat: return $node.getFloat
of JBool: return $node.getBool
of JNull: return ""
of JObject: return $node
of JArray: return $node
else: return default else: return default
@ -30,20 +41,20 @@ type HandleProcMsgCB* = proc (outMsg: TaintedString,
errMsg: TaintedString, cmd: string): void errMsg: TaintedString, cmd: string): void
proc sendMsg*(h: HandleProcMsgCB, outMsg: TaintedString, proc sendMsg*(h: HandleProcMsgCB, outMsg: TaintedString,
errMsg: TaintedString = nil, cmd: string = ""): void = errMsg: TaintedString = "", cmd: string = ""): void =
if h != nil: h(outMsg, errMsg, cmd) if h != nil: h(outMsg, errMsg, cmd)
proc makeProcMsgHandler*(outSink, errSink: File, prefixCmd: bool = true): HandleProcMsgCB = proc makeProcMsgHandler*(outSink, errSink: File, prefixCmd: bool = true): HandleProcMsgCB =
result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} = result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
let prefix = if cmd == nil or not prefixCmd: "" else: cmd & ": " let prefix = if cmd.len == 0 or not prefixCmd: "" else: cmd & ": "
if outMsg != nil: outSink.writeLine(prefix & outMsg) if outMsg.len > 0: outSink.writeLine(prefix & outMsg)
if errMsg != nil: errSink.writeLine(prefix & errMsg) if errMsg.len > 0: errSink.writeLine(prefix & errMsg)
proc makeProcMsgHandler*(outSink, errSink: Stream, prefixCmd: bool = true): HandleProcMsgCB = proc makeProcMsgHandler*(outSink, errSink: Stream, prefixCmd: bool = true): HandleProcMsgCB =
result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} = result = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
let prefix = if cmd == nil or not prefixCmd: "" else: cmd & ": " let prefix = if cmd.len == 0 or not prefixCmd: "" else: cmd & ": "
if outMsg != nil: outSink.writeLine(prefix & outMsg) if outMsg.len > 0: outSink.writeLine(prefix & outMsg)
if errMsg != nil: errSink.writeLine(prefix & errMsg) if errMsg.len > 0: errSink.writeLine(prefix & errMsg)
proc combineProcMsgHandlers*(a, b: HandleProcMsgCB): HandleProcMsgCB = proc combineProcMsgHandlers*(a, b: HandleProcMsgCB): HandleProcMsgCB =
if a == nil: result = b if a == nil: result = b
@ -61,9 +72,9 @@ proc waitFor*(p: Process, msgCB: HandleProcMsgCB, procCmd: string = ""): int =
var line = newStringOfCap(120).TaintedString var line = newStringOfCap(120).TaintedString
while true: while true:
if pout.readLine(line): if pout.readLine(line):
msgCB.sendMsg(line, nil, procCmd) msgCB.sendMsg(line, "", procCmd)
elif perr.readLine(line): elif perr.readLine(line):
msgCB.sendMsg(nil, line, procCmd) msgCB.sendMsg("", line, procCmd)
else: else:
result = peekExitCode(p) result = peekExitCode(p)
if result != -1: break if result != -1: break
@ -82,15 +93,15 @@ proc execWithOutput*(command: string, workingDir:string = "",
args: openArray[string] = [], env: StringTableRef = nil, args: openArray[string] = [], env: StringTableRef = nil,
options: set[ProcessOption] = {poUsePath}, options: set[ProcessOption] = {poUsePath},
msgCB: HandleProcMsgCB = nil): msgCB: HandleProcMsgCB = nil):
tuple[output: TaintedString, error: TaintedString, exitCode: int] = tuple[output, error: TaintedString, exitCode: int] =
result = (TaintedString"", TaintedString"", -1) result = (TaintedString"", TaintedString"", -1)
var outSeq, errSeq: seq[TaintedString] var outSeq, errSeq: seq[TaintedString]
outSeq = @[]; errSeq = @[] outSeq = @[]; errSeq = @[]
var outputCollector = combineProcMsgHandlers(msgCB, var outputCollector = combineProcMsgHandlers(msgCB,
proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} = proc(outMsg, errMsg: TaintedString, cmd: string): void {.closure.} =
if outMsg != nil: outSeq.add(outMsg) if outMsg.len > 0: outSeq.add(outMsg)
if errMsg != nil: errSeq.add(errMsg)) if errMsg.len > 0: errSeq.add(errMsg))
result[2] = exec(command, workingDir, args, env, options, outputCollector) result[2] = exec(command, workingDir, args, env, options, outputCollector)
result[0] = outSeq.join("\n") result[0] = outSeq.join("\n")
@ -136,15 +147,15 @@ proc daemonize*(pidfile, si, so, se: string, daemonMain: proc(): void): Pid =
flushFile(stdout) flushFile(stdout)
flushFile(stderr) flushFile(stderr)
if not si.isNil and si != "": if si.len > 0:
fi = open(si, fmRead) fi = open(si, fmRead)
discard dup2(getFileHandle(fi), getFileHandle(stdin)) discard dup2(getFileHandle(fi), getFileHandle(stdin))
if not so.isNil and so != "": if so.len > 0:
fo = open(so, fmAppend) fo = open(so, fmAppend)
discard dup2(getFileHandle(fo), getFileHandle(stdout)) discard dup2(getFileHandle(fo), getFileHandle(stdout))
if not se.isNil and so != "": if se.len > 0:
fe = open(se, fmAppend) fe = open(se, fmAppend)
discard dup2(getFileHandle(fe), getFileHandle(stderr)) discard dup2(getFileHandle(fe), getFileHandle(stderr))
@ -162,3 +173,39 @@ proc daemonize*(pidfile, si, so, se: string, daemonMain: proc(): void): Pid =
daemonMain() daemonMain()
return pid1 return pid1
const termReset* = "\e[0;m"
proc termColor*(color: TermColor, bright, bold = false): string =
var colorVal = ord(color)
if bright: inc(colorVal, 60)
return "\e[" & $colorVal & (if bold: ";1" else: "") & "m"
proc withColor*(str: string, color: TermColor, bright, bold = false): string =
return termColor(color, bright, bold) & str
let STRIP_ANSI_REGEX = re"\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]"
proc stripAnsi*(str: string): string = return str.replace(STRIP_ANSI_REGEX, "")
proc queryParamsToCliArgs*(queryParams: Table[string, string]): seq[string] =
result = @[]
for k,v in queryParams:
# support ?arg1=val1&arg2=val2 -> cmd val1 val2
if k.startsWith("arg"): result.add(v)
else :
result[1].add("--" & k)
if v != "true": result[1].add(v) # support things like ?verbose=true -> cmd --verbose
proc queryParamsToCliArgs*(queryParams: StringTableRef): seq[string] =
result = @[]
for k,v in queryParams:
# support ?arg1=val1&arg2=val2 -> cmd val1 val2
if k.startsWith("arg"): result.add(v)
else :
result[1].add("--" & k)
if v != "true": result[1].add(v) # support things like ?verbose=true -> cmd --verbose

View File

@ -1,11 +1,11 @@
# Package # Package
version = "0.3.4" version = "0.6.1"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Helper functions for writing command line interfaces." description = "Helper functions for writing command line interfaces."
license = "MIT" license = "MIT"
# Dependencies # Dependencies
requires @["nim >= 0.18.0", "docopt"] requires @["nim >= 0.19.0", "docopt >= 0.6.8"]