Skip to content

Instantly share code, notes, and snippets.

@luciusmagn
Created September 23, 2018 13:42
Show Gist options
  • Save luciusmagn/fa8a5673d9eaa872fd570fa14094cb6c to your computer and use it in GitHub Desktop.
Save luciusmagn/fa8a5673d9eaa872fd570fa14094cb6c to your computer and use it in GitHub Desktop.
# --- This file was borrowed from Picheta's nimble package manager
# --- Modifications: now it is kinda thread-safe, removal of testing output
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
#
# Rough rules/philosophy for the messages that Nimble displays are the following:
# - Green is only shown when the requested operation is successful.
# - Blue can be used to emphasise certain keywords, for example actions such
# as "Downloading" or "Reading".
# - Red is used when the requested operation fails with an error.
# - Yellow is used for warnings.
#
# - Dim for LowPriority.
# - Bright for HighPriority.
# - Normal for MediumPriority.
import logging, terminal, sets, strutils
type
CLI* = ref object
level: Priority
warnings: HashSet[(string, string)]
suppressionCount: int ## Amount of messages which were not shown.
showColor: bool ## Whether messages should be colored.
suppressMessages: bool ## Whether Warning, Message and Success messages
## should be suppressed, useful for
## commands like `dump` whose output should be
## machine readable.
Priority* = enum
DebugPriority, LowPriority, MediumPriority, HighPriority
DisplayType* = enum
Error, Warning, Message, Success
ForcePrompt* = enum
dontForcePrompt, forcePromptYes, forcePromptNo
const
longestCategory = len("Downloading")
foregrounds: array[Error .. Success, ForegroundColor] =
[fgRed, fgYellow, fgCyan, fgGreen]
styles: array[DebugPriority .. HighPriority, set[Style]] =
[{styleDim}, {styleDim}, {}, {styleBright}]
proc newCLI(): CLI =
result = CLI(
level: LowPriority,
suppressionCount: 0,
showColor: true,
suppressMessages: false
)
var threadCLI {.threadvar.}: CLI
proc maybe_init_cli() =
if threadCLI == nil:
threadCLI = newCLI();
proc calculateCategoryOffset(category: string): int =
assert category.len <= longestCategory
return longestCategory - category.len
proc displayCategory(category: string, displayType: DisplayType,
priority: Priority) =
maybe_init_cli()
# Calculate how much the `category` must be offset to align along a center
# line.
let offset = calculateCategoryOffset(category)
# Display the category.
let text = "$1$2 " % [spaces(offset), category]
if threadCLI.showColor:
if priority != DebugPriority:
setForegroundColor(stdout, foregrounds[displayType])
writeStyled(text, styles[priority])
resetAttributes()
else:
stdout.write(text)
proc displayLine(category, line: string, displayType: DisplayType,
priority: Priority) =
displayCategory(category, displayType, priority)
# Display the message.
writeStyled(line & "\n", styles[priority])
proc display*(category, msg: string, displayType = Message,
priority = MediumPriority) {.gcsafe.} =
maybe_init_cli()
# Don't print any Warning, Message or Success messages when suppression of
# warnings is enabled. That is, unless the user asked for --verbose output.
if threadCLI.suppressMessages and displayType >= Warning and
threadCLI.level == HighPriority:
return
# Suppress this message if its priority isn't high enough.
if priority < threadCLI.level:
if priority != DebugPriority:
threadCLI.suppressionCount.inc
return
# Display each line in the message.
var i = 0
for line in msg.splitLines():
if len(line) == 0: continue
displayLine(if i == 0: category else: "...", line, displayType, priority)
i.inc
proc displayDebug*(category, msg: string) =
## Convenience for displaying debug messages.
display(category, msg, priority = DebugPriority)
proc displayDebug*(msg: string) =
## Convenience for displaying debug messages with a default category.
displayDebug("Debug:", msg)
proc displayTip*() =
maybe_init_cli()
## Called just before Nimble exits. Shows some tips for the user, for example
## the amount of messages that were suppressed and how to show them.
if threadCLI.suppressionCount > 0:
let msg = "$1 messages have been suppressed, use --verbose to show them." %
$threadCLI.suppressionCount
display("Tip:", msg, Warning, HighPriority)
proc prompt*(forcePrompts: ForcePrompt, question: string): bool =
case forcePrompts
of forcePromptYes:
display("Prompt:", question & " -> [forced yes]", Warning, HighPriority)
return true
of forcePromptNo:
display("Prompt:", question & " -> [forced no]", Warning, HighPriority)
return false
of dontForcePrompt:
display("Prompt:", question & " [y/N]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let yn = stdin.readLine()
case yn.normalize
of "y", "yes":
return true
of "n", "no":
return false
else:
return false
proc promptCustom*(forcePrompts: ForcePrompt, question, default: string): string =
case forcePrompts:
of forcePromptYes:
display("Prompt:", question & " -> [forced " & default & "]", Warning,
HighPriority)
return default
else:
if default == "":
display("Prompt:", question, Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let user = stdin.readLine()
if user.len == 0: return promptCustom(forcePrompts, question, default)
else: return user
else:
display("Prompt:", question & " [" & default & "]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let user = stdin.readLine()
if user == "": return default
else: return user
proc promptCustom*(question, default: string): string =
return promptCustom(dontForcePrompt, question, default)
proc promptList*(forcePrompts: ForcePrompt, question: string, args: openarray[string]): string =
case forcePrompts:
of forcePromptYes:
result = args[0]
display("Prompt:", question & " -> [forced " & result & "]", Warning,
HighPriority)
else:
display("Prompt:", question & " [" & join(args, "/") & "]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
result = stdin.readLine()
for arg in args:
if arg.cmpIgnoreCase(result) == 0:
return arg
return promptList(forcePrompts, question, args)
proc setVerbosity*(level: Priority) =
threadCLI.level = level
proc setShowColor*(val: bool) =
threadCLI.showColor = val
proc setSuppressMessages*(val: bool) =
threadCLI.suppressMessages = val
import
asyncdispatch,
asyncfile,
db_postgres,
times,
strutils,
sequtils,
threadpool,
posix,
cli,
os
let file = open("remedias.log");
proc to_priority(s: string): Priority =
case s
of "LOW": LowPriority
of "MED": MediumPriority
of "HGH": HighPriority
else: LowPriority
proc eliminate_tokens() {.async.} =
while true:
if now().hour == 4 and now().hour == 0 and now().second == 0 and not file_exists(".db-lock"):
discard open(".db-lock", fmWrite)
echo "eliminating"
let
db = open("localhost", "db", "test", "Meedias")
rows = db.get_all_rows(sql"SELECT id, valid_tokens FROM users");
for row in rows:
let id = row[0];
let tokens = row[1]
.replace("{", "")
.replace("}", "")
.split(',');
let res =
if tokens.len() > 1:
'{' & tokens[1..<tokens.len()].foldl(a & ',' & b) & '}'
else:
"{}"
db.exec(sql"UPDATE users SET valid_tokens=? WHERE id=?", res, id);
display("daemon", "eliminated tokens")
db.close();
sleep 1000
discard unlink(".db-lock")
sleep 23 * 3600 * 1000;
proc logger() {.async.} =
while true:
try:
echo "reading";
let line = file.read_line();
let words = line.split(' ');
case words[0]
of "REQ":
display("Request", words[1..<words.len()].join(" "), Success, HighPriority)
of "INF":
display("Info", words[2..<words.len()].join(" "), Message, words[1].to_priority())
of "ERR":
display("Error", words[2..<words.len()].join(" "), Error, words[1].to_priority())
of "WRN":
display("Warning", words[2..<words.len()].join(" "), Warning, words[1].to_priority())
of "FTL":
display("Fatal", words[2..<words.len()].join(" "), Error, words[1].to_priority())
except:
sleep 300
if prompt(dont_force_prompt, "start daemon?"):
display("daemon", "eliminating tokens at 4 AM", Success, HighPriority);
discard spawn eliminate_tokens();
waitfor logger();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment