Current section
Files
Jump to
Current section
Files
src/typeid/uuid.gleam
import gleam/int
import gleam/string
import gleam/time/timestamp
@external(erlang, "crypto", "strong_rand_bytes")
@external(javascript, "../typeid.ffi.mjs", "strongRandomBytes")
fn strong_random_bytes(a: Int) -> BitArray
pub fn v7() -> String {
let #(sec, ns) =
timestamp.system_time()
|> timestamp.to_unix_seconds_and_nanoseconds()
let stamp = sec * 1000 + ns / 1_000_000
let assert <<a:size(12), b:size(62), _:size(6)>> = strong_random_bytes(10)
let value = <<stamp:48, 7:4, a:12, 2:2, b:62>>
to_string(value)
}
pub fn to_string(uuid: BitArray) -> String {
do_to_string(uuid, 0, "")
}
pub fn from_string(str: String) -> Result(BitArray, Nil) {
do_to_bit_array(str, 0, <<>>)
}
fn do_to_string(ints: BitArray, position: Int, acc: String) -> String {
case position {
8 | 13 | 18 | 23 -> do_to_string(ints, position + 1, acc <> "-")
_ ->
case ints {
<<i:size(4), rest:bits>> -> {
let string = int.to_base16(i) |> string.lowercase
do_to_string(rest, position + 1, acc <> string)
}
_ -> acc
}
}
}
fn do_to_bit_array(
str: String,
index: Int,
acc: BitArray,
) -> Result(BitArray, Nil) {
case string.pop_grapheme(str) {
Error(Nil) if index == 32 -> Ok(acc)
Ok(#("-", rest)) if index < 32 -> do_to_bit_array(rest, index, acc)
Ok(#(c, rest)) if index < 32 ->
case hex_to_int(c) {
Ok(i) -> do_to_bit_array(rest, index + 1, <<acc:bits, i:size(4)>>)
Error(_) -> Error(Nil)
}
_ -> Error(Nil)
}
}
fn hex_to_int(c: String) -> Result(Int, Nil) {
let i = case c {
"0" -> 0
"1" -> 1
"2" -> 2
"3" -> 3
"4" -> 4
"5" -> 5
"6" -> 6
"7" -> 7
"8" -> 8
"9" -> 9
"a" | "A" -> 10
"b" | "B" -> 11
"c" | "C" -> 12
"d" | "D" -> 13
"e" | "E" -> 14
"f" | "F" -> 15
_ -> 16
}
case i {
16 -> Error(Nil)
x -> Ok(x)
}
}