87 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Nim
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Nim
		
	
	
	
	
	
| # 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 <reference> [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 <format>  Select a specific output format. Valid values
 | |
|                                 are 'raw', 'markdown', 'plain'.
 | |
| 
 | |
|   -t, --esv-api-token <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["<reference>"]
 | |
| 
 | |
|     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)
 |