
Hey! Today I share with you 5 simple packages, I used to be on mute all the time, but now I want to share more of the stuff I do, so in case there are bored people that want to learn/contribute (like this guy here ), they can find something to work on π
This is using the Monocypher library. It provides low-level and high-level APIs for working with secret things, passwords, csrf, tokens, random bytes.
import e2ee/password
let pwd = generatePassword("masterpassword", generateSalt().toHex(), length = 16)
echo "Generated password: ", pwd
Here, an example where Alice and Bob share top secret stuff
This is a simple password strength estimator, that works by checking the password against based on multiple factors: length, character variety, repetition, leet substitution, and optionally, a list of common passwords (you must provide the list yourself, example here).
import blackpaper
let res: PasswordStrengthResult = passwordStrength("P@ssw0rd123")
echo "Score: ", res.score
echo "Strength: ", res.strength # Weak
echo "Reason: ", res.reason # TooPredictable
This is a large database of mime types, useful for retrieving the mime info of a file based on file extension, or content type. Basically, you will need this if you want to implement a static file server or anything that needs to work with files and their mime types
import mimedb
assert getMimeType("html") == some("text/html")
let info2 = getMimeInfo("application/zip")
assert info2.isCompressible == false
assert info2.getExtensions() = some(@["zip"])
assert isExtension("m4a")
This is a wrapper around the standalone Impeller Engine (used by Flutter) via the C API. Currently the high-level API is still in early stages, but the low-level bindings are already available.
I see many CLI frameworks out there. Let me share my own! This one has types, is using macros for the syntax/command definition, can automatically align the help/usage text, has support for labels, auto-links the command to the callback and soon enough, will support pluggable commands!
Pluggable commands will be shared libraries that can be loaded at runtime, so you can have a core CLI app and then load commands per project basis. Imagine a database CLI where you can run migrations, seed data, run queries per project
import pkg/kapsis
#
# Define your command handlers here
#
proc helloCommand(v: Values) =
echo v.get("pkgname").getStr
proc greetCommand(v: Values) =
if v.has("greeting"):
echo v.get("greeting").getStr
echo v.get("name").getStr
proc colorsOrangeCommand(v: Values) =
echo "The one that is orangely out of its head"
proc colorsBlueCommand(v: Values) =
echo "Now everyone loves the new blue / Cause itβs the truest"
proc colorsWhateverColorCommand(v: Values) =
echo "Whatever color command"
#
# Init Kapsis with the defined commands
#
initKapsis do:
commands do:
-- "Crazy stuff"
hello name.string, int(age), ?bool(verbose):
## Describe your command here
-- "Another command"
greet name.string, ?string(greeting):
## Greeting someone with an optional greeting message
-- "Colors by Ken Nordine"
colors:
## Colors are cool, let's have some fun with them
blue bool(enable):
## Blue was the bluest blue can be blue
orange bool(enable):
## The silly old color who lives next to red Yup! Doc comments are used as command description. Crazy!
A plain preview of the generated app (description, author, license and version are collected from the .nimble file):
Your type of CLI framework
(c) George Lemon | MIT License
Build Version: 0.3.0
Crazy stuff
hello <name:string> <age:int> <?verbose:bool> Describe your command here
Another command
greet <name:string> <?greeting:string> Greeting someone with an optional greeting message
Colors by Ken Nordine
colors.blue <enable:bool> Blue was the bluest blue can be blue
colors.orange <enable:bool> The silly old color who lives next to red
colors.all Get all the colors!
--sure:bool
Instead of creating a new thread for each package, I decided to share them all in one place, this is my Another Day Another Package thread, where I will share all the small packages I create. If you have any ideas, suggestions or want to contribute fork these repos like a hungry beast!
I'm always open to new ideas! My open source work is a therapy for me, let's make the Nim's ecosystem a better place together! π€
This is great if you want to have a fully working PostgreSQL instance for testing and development without the need to install it on your machine. Basically, it creates disposable Postgres instances that you can start, interact with, stop and delete. Combine it with the pkg/db_connector to get that juice!
const pqlibPath = currentSourcePath().parentDir / "greskewelbox" / "bin" / "16.9.0" / "darwin" / "lib"
const greskewel_lib = pqlibPath / "libpq.dylib"
{.push dynlib: greskewel_lib.}
let db = open("localhost", "postgres", "postgres", "postgres")
db.exec(sql"""CREATE TABLE myTable (id integer, name varchar(50) not null)""")
# ...
{.pop.}
Sure, this should provide a more fancy API for wrapping the push/pop block, with automatically finding the library based on version, machine type and OS.
Example setup here, and example usage here. A similar implementation of this package can be found for go, java and rust.
This is a small ORM that provides a fancy API for working with databases. It uses the macro system for generating and modifying code, together with macrocache (for storing the generated models and their metadata) + pkg/parsesql package for parsing and validating SQL queries at compile time.
newModel Users:
id {.pk.}: Serial
username {.unique.}: Varchar(50)
name: Varchar(100)
email {.notnull, unique.}: Varchar(100)
created_at {.notnull.}: TimestampTz
newModel SubscriptionPlan:
id {.pk.}: Serial
name {.notnull, unique.}: Varchar(50)
price {.notnull.}: Money
newModel Subscriptions:
id {.pk.}: Serial
user_id: Users.id
plan {.notnull.}: SubscriptionPlan.id
created_at {.notnull.}: TimestampTz
# all generated models come with some predefined getters,
# like getName, getEmail, etc. based on the field names.
I'm using the magic pkg/threading/once. So, for running any database operations you need either withDB or withDBPool block. The first one provides a single connection to the database (closing the connection after the block), while the second one provides a connection pool.
withDB do:
Models.table(Users).prepareTable().exec()
Models.table(SubscriptionPlan).prepareTable().exec()
Models.table(Subscriptions).prepareTable().exec()
Example using withDBPool
withDBPool do:
let id = Models.table(Users).insert({
"username": "johndoe",
"name": "John Doe",
"email": "[email protected]"
"created_at": $now()
}).execGet()
# one more
let res = Models.table(Users)
.selectAll()
.where("email", "[email protected]")
.get()
# res is a Collection[T], a thin wrapper with some additional runtime helpers
let user = res.first()
assert user.getEmail == "[email protected]"
Resumable queries are also supported, so you can write your query in multiple lines and execute it when it's ready.
Here, more examples, repo and API reference:
Look into Ormin how to do DSL design. A lot of thought has been put it into it and
Models.table(Users)
.selectAll()
.where("email", "[email protected]")
.get()
is inferior. But ymmv, differences in taste are a thing.
Better cake today and sleep tomorrow π
I discovered a package that implements a Red-Black tree in Nim, is 11-years old! And is compiling just fine. Nim ecosystem has some hidden gems! And... I couldn't resist, so I dropped that into my chatbot and we started to talk sh*t together, and there you go!
This is Boogie, a stupid simple embeddable in-memory WAL-based database. yeah, it's a fluffy toy, but I think it's a good starting point for building something more complex in the future
Here, some numbers: https://github.com/openpeeps/boogie/actions/runs/24114137984/job/70354681486#step:6:9
Cheers!
Aaand sleep today! Hahaa I got too excited by that vintage package.
I gotta read more about databases and how they work. More numbers WAL + mem or disk. Sure, no WAL is way slower on inserting.
[Suite] WAL + disk store tests
Database opened in 0.001 seconds
[OK] init database without WAL
[OK] create table
Insert: 0.447 s for 10000 rows
[OK] insert rows
Lookup: 0.039 s for 10000 gets (hits=10000)
[OK] lookup rows
Ordered scan: 0.375 s for 10000 rows
[OK] ordered scan
Unsorted scan: 0.035 s for 10000 rows
[OK] ordered scan #2
Where scan: 0.023 s for 334 matches
[OK] where scan
[Suite] WAL + memory store tests
Database opened in 0.000 seconds
[OK] init database without WAL
[OK] create table
Insert: 0.418 s for 10000 rows
[OK] insert rows
Lookup: 0.038 s for 10000 gets (hits=10000)
[OK] lookup rows
Ordered scan: 0.371 s for 10000 rows
[OK] ordered scan
Unsorted scan: 0.031 s for 10000 rows
[OK] ordered scan #2
Where scan: 0.023 s for 334 matches
[OK] where scan
I'm curious about how SQLite performs on similar workloads