diff --git a/configlet.nimble b/configlet.nimble index b6a906bf..7f11c537 100644 --- a/configlet.nimble +++ b/configlet.nimble @@ -23,15 +23,6 @@ requires "cligen#b962cf8bc0be847cbc1b4f77952775de765e9689" # 1.5.19 (2021-0 requires "jsony#2a2cc4331720b7695c8b66529dbfea6952727e7b" # 1.1.3 (2022-01-03) requires "parsetoml#6e5e16179fa2db60f2f37d8b1af4128aaa9c8aaf" # 0.7.1 (2023-08-06) requires "supersnappy#e4df8cb5468dd96fc5a4764028e20c8a3942f16a" # 2.1.3 (2022-06-12) -requires "uuids#8cb8720b567c6bcb261bd1c0f7491bdb5209ad06" # 0.1.11 (2021-01-15) -# To make Nimble use the pinned `isaac` version, we must pin `isaac` after `uuids` -# (which has `isaac` as a dependency). -# Nimble still clones the latest `isaac` tag if there is no tag-versioned one -# on-disk (e.g. at ~/.nimble/pkgs/isaac-0.1.3), and adds it to the path when -# building, but (due to writing it later) the pinned version takes precedence. -# Nimble will support lock files in the future, which should provide more robust -# version pinning. -requires "isaac#45a5cbbd54ff59ba3ed94242620c818b9aad1b5b" # 0.1.3 (2017-11-16) task test, "Runs the test suite": exec "nim r ./tests/all_tests.nim" diff --git a/src/create/approaches.nim b/src/create/approaches.nim index 987ccc91..23186a13 100644 --- a/src/create/approaches.nim +++ b/src/create/approaches.nim @@ -1,6 +1,6 @@ import std/[os, strformat, strutils] -import pkg/[jsony, uuids] -import ".."/[helpers, sync/sync_common, types_track_config, types_approaches_config] +import pkg/jsony +import ".."/[helpers, sync/sync_common, types_track_config, types_approaches_config, uuid/uuid] func kebabToTitleCase(slug: Slug): string = result = newStringOfCap(slug.len) @@ -44,7 +44,7 @@ proc createApproach*(approachSlug: Slug, exerciseSlug: Slug, if not approachExists: config.approaches.add ApproachConfig( - uuid: $genUUID(), + uuid: $genUuid(), slug: $approachSlug, title: title, blurb: "", diff --git a/src/create/articles.nim b/src/create/articles.nim index 08280d90..1e170816 100644 --- a/src/create/articles.nim +++ b/src/create/articles.nim @@ -1,6 +1,6 @@ import std/[os, strformat, strutils] -import pkg/[jsony, uuids] -import ".."/[helpers, sync/sync_common, types_track_config, types_articles_config] +import pkg/jsony +import ".."/[helpers, sync/sync_common, types_track_config, types_articles_config, uuid/uuid] func kebabToTitleCase(slug: Slug): string = result = newStringOfCap(slug.len) @@ -40,7 +40,7 @@ proc createArticle*(articleSlug: Slug, exerciseSlug: Slug, if not articleExists: config.articles.add ArticleConfig( - uuid: $genUUID(), + uuid: $genUuid(), slug: $articleSlug, title: title, blurb: "", diff --git a/src/uuid/uuid.nim b/src/uuid/uuid.nim index e9ba33b9..ab2b40de 100644 --- a/src/uuid/uuid.nim +++ b/src/uuid/uuid.nim @@ -1,7 +1,30 @@ -import std/strformat -import pkg/uuids +import std/[strformat, sysrand] import ".."/logger +type + Uuid* = array[16, byte] + +proc genUuid*: Uuid {.noinit.} = + ## Returns a version 4 UUID, using the system CSPRNG as the source of randomness. + if urandom(result): + result[6] = (result[6] and 0x0f) or 0x40 # Set version to 4 + result[8] = (result[8] and 0x3f) or 0x80 # Set variant to 1 + else: + stderr.writeLine "uuid: error: failed to read from the system CSPRNG" + quit 1 + +func `$`*(u: Uuid): string = + ## Returns the canonical string representation for the given UUID `u`. + result = newString(36) + result[8] = '-' + result[13] = '-' + result[18] = '-' + result[23] = '-' + for i, j in [0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34]: + const hex = "0123456789abcdef" + result[j + 0] = hex[u[i] shr 4] + result[j + 1] = hex[u[i] and 0x0f] + proc outputUuids(n: Positive) = ## Writes `n` version 4 UUIDs to stdout. Writes only 1000 UUIDs if `n` is ## greater than 1000. @@ -12,7 +35,7 @@ proc outputUuids(n: Positive) = let numUuidsToGenerate = min(n, maxNumUuids) var s = newStringOfCap(numUuidsToGenerate * 37) for i in 1 .. numUuidsToGenerate: - s.add $genUUID() + s.add $genUuid() s.add '\n' stdout.write s diff --git a/tests/test_uuid.nim b/tests/test_uuid.nim index 4f257b5f..abfaa767 100644 --- a/tests/test_uuid.nim +++ b/tests/test_uuid.nim @@ -1,13 +1,19 @@ -import std/unittest -import pkg/uuids -import "."/lint/validators +import std/[sets, strformat, strutils, unittest] +import "."/[lint/validators, uuid/uuid] proc main = - suite "genUUID: returns a string that isUuidV4 says is valid": - test "1000 UUIDs": - for i in 1 .. 1000: - let uuid = $genUUID() - check isUuidV4(uuid) + suite "genUuid": + const n = 100_000 + var uuids = initHashSet[Uuid](n) + + test &"can generate {insertSep($n)} valid version 4 UUIDs": + for i in 1 .. n: + let uuid = genUuid() + check isUuidV4($uuid) + uuids.incl uuid + + test "those UUIDs are distinct": + check uuids.len == n main() {.used.}