pathsd/src/pathsd.nim
2023-12-05 21:15:10 +01:00

121 lines
3.4 KiB
Nim

import std/[algorithm, dirs, logging, options, paths, strutils, syncio]
import strformat
import commandant
type
ErrorCode = typeof(QuitSuccess)
MainResult* = object
output*: seq[string]
errorCode*: ErrorCode
const version = "1.0.1"
const usage_string = """usage: pathsd [--version | --help] [-v | -q] [-s shell] search_path ..."""
const version_string = fmt"""pathsd {version}"""
const help_string = fmt"""pathsd {version} by BTHLabs
Developed by Tomek Wójcik <contact@btlabs.pl> (https://bthlabs.pl/)
(c) 2023-present by BTHLabs | MIT License
Usage:
pathsd [options] search_path ...
Options:
-s --shell target shell (defaults to bash)
-v --verbose turn debugging messages on
-q --quiet supress all logging messages
--version show the version
--help show this help"""
const QuitInvalidArgs: ErrorCode = 64
var logger*: ConsoleLogger = newConsoleLogger(
fmtStr = "$datetime $appname $levelname: ",
levelThreshold = lvlInfo,
useStderr = true,
)
proc handleCommandantError(reason: ExitReason,
msg: string = "",
token: Option[CmdToken] = none(CmdToken),
) =
case reason
of ExitReason.missingArgumentValue, ExitReason.missingOptionValue:
quit(usage_string, QuitInvalidArgs)
of ExitReason.exception:
logger.log(lvlError, msg)
quit(QuitFailure)
proc readPart(path: Path): seq[string] =
logger.log(lvlDebug, fmt"Processing part: {path.string}")
var pathFile: File = open(path.string)
result = @[]
try:
var line: string = readLine(pathFile)
while line != "":
result.add(line)
line = readLine(pathFile)
except EOFError as exception:
discard
finally:
close(pathFile)
return result
proc main*(searchPaths: seq[string]): MainResult =
var parts: seq[string] = @[]
logger.log(lvlDebug, fmt"Processing search paths: {searchPaths}")
for searchPath in searchPaths:
var searchPathPath: Path = Path(searchPath)
if not dirExists(searchPathPath):
logger.log(lvlNotice, fmt"Skipping {searchPath}: does not exist?")
continue
var partPaths: seq[Path] = @[]
for pathComponent, path in walkDir(searchPathPath, true, false, true):
if pathComponent in [PathComponent.pcFile, PathComponent.pcLinkToFile]:
partPaths.add(searchPathPath / path)
partPaths.sort do (x: Path, y: Path) -> int:
result = cmp(x.string, y.string)
for partPath in partPaths:
parts = parts & readPart(partPath)
return MainResult(output: parts, errorCode: QuitSuccess)
when isMainModule:
commandline:
arguments(searchPaths, string, true)
commandant.option(shell, string, "shell", "s", "bash")
flag(verbose, "verbose", "v")
flag(quiet, "quiet", "q")
exitoption("help", "h", help_string, QuitFailure)
exitoption("version", "", version_string, QuitFailure)
errorproc(handleCommandantError)
var loggerLevel: Level = lvlInfo
if quiet:
loggerLevel = lvlNone
elif verbose:
loggerLevel = lvlDebug
logger.levelThreshold = loggerLevel
var mainResult = main(searchPaths)
if mainResult.output != @[] and mainResult.errorCode == QuitSuccess:
var output: string = ""
case shell
of "bash":
let pathComponents = join(mainResult.output, ":")
output = fmt"""export PATH="{pathComponents}":$PATH"""
if output != "":
echo(output)
quit(mainResult.errorCode)