plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-std/Uuid.plum
module std
import std/Number
import std/Result
import std/Str
import std/Bool
# A UUID, stored as its canonical 36-character
# `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form.
enum Uuid(ToStr) =
| Uuid(value: Str)
fun toStr(self) -> Str =
return self.value
fun equals(self, other: Uuid) -> Bool =
return self.value == other.value
fun isNil(self) -> Bool =
return self.value == NIL
# The version nibble (1-5 for a well-formed RFC 4122 UUID), the first hex
# digit of the third group (`xxxxxxxx-xxxx-Vxxx-...`), at string index 14.
fun version(self) -> Int =
return hexValue(self.value.byteAt(14))
# The variant bits, decoded from the high bits of the first hex digit of
# the fourth group (`...-Nxxx-...`), at string index 19. Returns 0 for the
# NCS-backward-compatible variant (`0xxx`), 2 for the RFC 4122 variant
# (`10xx`, what `v4` produces), 6 for Microsoft's (`110x`), 7 for the
# reserved future variant (`111x`).
fun variant(self) -> Int =
n := hexValue(self.value.byteAt(19))
if n & 0x8 == 0
return 0
if n & 0x4 == 0
return 2
if n & 0x2 == 0
return 6
return 7
NIL = "00000000-0000-0000-0000-000000000000"
fun nil() -> Uuid =
return Uuid(value: NIL)
# Parses the canonical `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` text form
# (case-insensitive), returning `Err` if `s` isn't exactly that shape.
fun fromStr(s: Str) -> Result[Uuid, Str] =
if s.length() != 36
return Err("invalid UUID length: '{s}'")
i := 0
while i < 36
if i == 8 || i == 13 || i == 18 || i == 23
# '-'
if s.byteAt(i) != 45
return Err("invalid UUID: '{s}'")
else
if hexValue(s.byteAt(i)) < 0
return Err("invalid UUID: '{s}'")
i = i + 1
return Ok(Uuid(value: s.toLower()))
fun isValid(s: Str) -> Bool =
match fromStr(s)
Ok(_) => True
Err(_) => False
# The numeric value (0-15) of an ASCII hex digit byte, or -1 if it isn't one.
fun hexValue(byte: Int) -> Int =
# '0'-'9'
if byte >= 48 && byte <= 57
return byte - 48
# 'a'-'f'
if byte >= 97 && byte <= 102
return byte - 97 + 10
# 'A'-'F'
if byte >= 65 && byte <= 70
return byte - 65 + 10
return -1
# A random (version 4, RFC 4122) UUID.
fun v4() -> Uuid =
a := rawRandomInt()
b := rawRandomInt()
time_low := { a >> 32 } & 0xFFFFFFFF
time_mid := { a >> 16 } & 0xFFFF
time_hi_and_version := { a & 0x0FFF } | 0x4000
clock_seq := { { b >> 48 } & 0x3FFF } | 0x8000
node := b & 0xFFFFFFFFFFFF
s := hexDigits(time_low, 8) + "-" + hexDigits(time_mid, 4) + "-"
+ hexDigits(time_hi_and_version, 4) + "-" + hexDigits(clock_seq, 4) + "-"
+ hexDigits(node, 12)
return Uuid(value: s)
fun hexDigit(n: Int) -> Str =
d := n & 0xF
if d < 10
return digitChar(d)
match d
10 => "a"
11 => "b"
12 => "c"
13 => "d"
14 => "e"
_ => "f"
# The low `count` hex digits of `n`, most-significant first.
fun hexDigits(n: Int, count: Int) -> Str =
if count <= 0
return ""
return hexDigits(n >> 4, count - 1) + hexDigit(n)
test "nil is the all-zero UUID and is recognized as such"
assert nil().toStr() == "00000000-0000-0000-0000-000000000000"
assert nil().isNil() == True
test "v4 produces a valid version-4, variant-2 UUID"
u := v4()
assert isValid(u.toStr()) == True
assert u.version() == 4
assert u.variant() == 2
assert u.isNil() == False
test "fromStr parses a valid UUID and normalizes case"
match fromStr("550E8400-E29B-41D4-A716-446655440000")
Ok(u) =>
assert u.toStr() == "550e8400-e29b-41d4-a716-446655440000"
Err(_) =>
assert True == False
test "fromStr rejects malformed input"
assert isValid("not-a-uuid") == False
assert isValid("550e8400-e29b-41d4-a716-44665544000") == False
assert isValid("550e8400xe29b-41d4-a716-446655440000") == False
test "equals compares by value"
a := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
b := Uuid(value: "550e8400-e29b-41d4-a716-446655440000")
assert a.equals(b) == True
assert a.equals(nil()) == False