# Nim CLI Wrapper for the ESV API # © 2023 Jonathan Bernard ## Simple command-line wrapper around the ESV API. import std/[httpclient, json, logging, os, re, strutils, uri, wordwrap] import cliutils, docopt, zero_functional proc formatMarkdown(raw: string): string = let rawLines = raw.splitLines --> map(it.strip) let wrapped = (rawLines --> filter(not isEmptyOrWhitespace(it.strip) and match(it, re"^\[\d+\].*")). map(it.multiReplace([(re"\((\d+)\)", ""), (re"\[(\d+)\]", "**$1**")])). map(wrapWords(it, maxLineWidth = 74, newLine = "\p"))). join("\p") result = (wrapped.splitLines --> map("> " & it)).join("\p") & "\p>\p> -- *" & rawLines[0] & " (ESV)*" when isMainModule: const USAGE = """Usage: esv_api [options] Options: --debug Log debug information. --echo-args Echo back the arguments that were passed on the command line for debugging purposes. -f, --output-format Select a specific output format. Valid values are 'raw', 'markdown', 'plain'. -t, --esv-api-token Provide the API token on the command line. By default this will be read either from the .esv_api.cfg.json file or the ESV_API_TOKEN envionment variable. """ let consoleLogger = newConsoleLogger( levelThreshold=lvlInfo, fmtStr="esv_api - $levelname: ") logging.addHandler(consoleLogger) try: # Parse arguments let args = docopt(USAGE, version = "0.2.0") if args["--debug"]: consoleLogger.levelThreshold = lvlDebug if args["--echo-args"]: stderr.writeLine($args) let cfgFilePath = getEnv("HOME") / ".esv_api.cfg.json" var cfgFileJson = newJObject() if fileExists(cfgFilePath): debug "Loading config from " & cfgFilePath cfgFileJson = parseFile(cfgFilePath) let cfg = CombinedConfig(docopt: args, json: cfgFileJson) let apiToken = cfg.getVal("esv-api-token") let apiRoot = cfg.getVal("esv-api-root", "https://api.esv.org") let reference = $args[""] let http = newHttpClient() http.headers = newHttpHeaders({"Authorization": "Token " & apiToken}) let urlPath = apiRoot & "/v3/passage/text/?q=" & encodeUrl(reference) debug "requesting " & urlPath let respJson = parseJson(http.getContent(urlPath)) let formattedPassages = case $args["--output-format"]: of "text": respJson["passages"].getElems --> map(it.getStr.multiReplace([(re"\[(\d+)\]", "$1")])) of "raw": respJson["passages"].getElems --> map(it.getStr) else: respJson["passages"].getElems --> map(formatMarkdown(it.getStr)) echo formattedPassages.join("\p") except CatchableError: fatal getCurrentExceptionMsg() debug getCurrentException().getStackTrace() quit(QuitFailure)