TSFoster/elm-uuid/2.0.1

NOTE: the latest version is 4.3.1

UUID

A UUID looks something like e1631449-6321-4a58-920c-5440029b092e, and can be used as an identifier for anything. Each 128-bit number, usually represented as 32 hexadecimal digits, is generally assumed to be Universally Unique (hence the name).

type UUID

This modules provides a UUID type, and functions to work with them. It is an opaque type, which basically means you have to use the provided functions if you want to do anything with it!

Here is an example of a Book model, which uses UUIDs to identify both the book and its authors:

type alias Book =
    { title : String
    , uuid : UUID
    , published : Maybe Date
    , authors : List UUID
    }

Creating UUIDs

UUIDs have version numbers (1-5), which describe how they were created, and variant numbers (1-2) which describes how they are stored. This module can read all UUIDs, but can only currently create versions 3 (a namespaced, heirarchical system) and 4 (a randomly-generated UUID). It creates variant 1 UUIDs (probably the best choice if you don't have a specific need for variant 2), but can convert them to variant 2.

Nil UUID

One type of UUID not defined above is the nil UUID. This is a "special form of UUID" defined as "00000000-0000-0000-0000-000000000000". I suppose it can be used as a placeholder for something that doesn't have a UUID yet?

Random UUIDs (Version 4)

Randomly-generated UUIDs are called version 4 UUIDs. This package provides a Random.Generator for the elm/random library.

generator : Generator UUID

Generating a random UUID is, I think, the most straightforward way of making a UUID, and I see them used all the time. There are a couple of ways of using a generator to create a value, which are described nicely in the elm/random docs. Here is an example of how you might use the UUID generator:

type Comment = Comment String UUID

makeComment : String -> Random.Seed -> ( Comment, Random.Seed )
makeComment comment seed =
    UUID.generator
        |> Random.map (Comment comment)
        |> Random.step seed

Hierarchical, namespaced UUIDs (Version 3, Version 5)

UUIDs can be created using a namespace UUID and a name, which is then hashed to create a new UUID. The hash function used depends on the version of UUID: verison 3 UUIDs use MD5, and version 5 UUIDs use SHA-1. Currently, this package can only create version 3 UUIDs.

Once generated, these UUIDs can then be used as a namespace, making a hierarchy! I think this is pretty cool! You can use this method for making predictable UUIDs from data, and it also has the added bonus that you don't have to deal with random generators/seeds.

v3ChildNamed : String -> UUID -> UUID

Start with an existing UUID as a "parent" UUID, and provide a name to create a new UUID.

grandparent = nil
parent = grandparent |> v3ChildNamed "parent"
parentsSibling = grandparent |> v3ChildNamed "parent's sibling"
child1 = parent |> v3ChildNamed "child1"
child2 = parent |> v3ChildNamed "child2"
cousin = parentsSibling |> v3ChildNamed "cousin"


UUID.canonical child2 == "4cacaf93-fcc5-3a02-bc41-c0a3e359e11d"

The RFC defining UUIDs defines some base UUIDs to start your hierarchy.

A UUID for the DNS namespace, "6ba7b810-9dad-11d1-80b4-00c04fd430c8".

UUID.dns |> v3ChildNamed "elm-lang.org"

A UUID for the URL namespace, "6ba7b811-9dad-11d1-80b4-00c04fd430c8"

UUID.url |> v3ChildNamed "https://package.elm-lang.org"

A UUID for the ISO object ID (OID) namespace, "6ba7b812-9dad-11d1-80b4-00c04fd430c8". I am not going to try to explain what an OID is, if you need to use this, you probably have a better grasp of them than I do!

UUID.oid |> v3ChildNamed "1.2.250.1"

A UUID for the X.500 Distinguished Name (DN) namespace, "6ba7b814-9dad-11d1-80b4-00c04fd430c8". I am not going to try to explain what a X.500 DN is, if you need to use this, you probably have a better grasp of them than I do!

Making variant 2 UUIDs

You may have noticed that generator and v3ChildNamed make variant 1 UUIDs. If you need to create a variant 2 UUID, any UUID can be converted to a variant 2 UUID with toVariant2. Note that variant 2 UUIDs cannot be converted to variant 1 UUIDs.

Variant 2 UUIDs are very similar to variant 1 UUIDs, the main end-user difference being that they provide 1 fewer bit of randomness, but are a Microsoft standard. Note the single digit change in the following example:

-- c72c207b-0847-386d-bdbc-2e5def81cf81
var1UUID =
    nil |> v3ChildNamed "hello world"




-- c72c207b-0847-386d-ddbc-2e5def81cf81
var2UUID =
    var1UUID |> toVariant2

Existing UUIDs

fromString : String -> Result String UUID

You can attempt to create a UUID from a string. This function can interpret a fairly broad range of formatted (and mis-formatted) UUIDs, including ones with too much whitespace, too many (or not enough) hyphens, or uppercase characters.

fromString "c72c207b-0847-386d-bdbc-2e5def81cf811" == Err "UUID was not correct length"


fromString "c72c207b-0847-386d-bdbc-2e5def81cg81" == Err "UUID contained non-hexadecimal digits"


fromString "00000000-0000-0000-0000-000000000000" == Ok nil


fromString "urn:uuid:00000000-0000-0000-0000-000000000000" == Ok nil


fromString "{00000000-0000-0000-0000-000000000000}" == Ok nil


fromString "\n\n     {urn:uuid: 00  000000-0000-0000-0-000-000000000000}" == Ok nil

Note: if you are decoding from JSON, you may like json-extra's fromResult function.

uuidDecoder = Json.Decode.string |> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.fromString)

Formatting

UUIDs are generally represented by 32 hexadecimal digits in the form 00112233-4455-M677-N899-aabbccddeeff, where the four bits at position M denote the UUID version, and the first two or three bits at position N denote the variant. This is the "canonical" representation, but there is also a Microsoft GUID representation and a URN representation.

toString : UUID -> String

This is just an alias for canonical, the most common way to represent a UUID.

canonical someUUID == toString someUUID
canonical : UUID -> String

Convert UUID to canonical textual representation

canonical nil == "00000000-0000-0000-0000-000000000000"
microsoftGUID : UUID -> String

Convert UUID to Microsoft GUID representation

microsoftGUID nil == "{00000000-0000-0000-0000-000000000000}"
urn : UUID -> String

Convert UUID to URN-namespaced representation

urn nil == "urn:uuid:00000000-0000-0000-0000-000000000000"

Inspecting UUIDs

Sometimes you may need to check a UUID's version, variant, or whether it's nil.

isNil : UUID -> Bool

True if the given UUID is "00000000-0000-0000-0000-000000000000".

version : UUID -> Maybe Int

If the bits of the UUID indicate that it is properly versioned UUID e.g. version 1, returns e.g. Just 1. Otherwise, Nothing!

version nil == Nothing


version (nil |> v3ChildNamed "Hello") == Just 3
isVersion : Int -> UUID -> Bool

True if the given UUID is the given version number. Always False is the integer provided is not 1, 2, 3, 4 or 5!

isVersion 8 someUUID -- always False!


isVersion 4 nil -- False!


someJsonValue
    |> Json.Decode.decodeValue UUID.decoder
    |> UUID.isVersion 5


isVersion 3 (nil |> v3ChildNamed "hello") -- True
variant : UUID -> Maybe Int

If the bits of the UUID indicate that it is a variant 1 or 2 UUID, returns Just 1 or Just 2, respectively. Otherwise, Nothing!

variant nil == Nothing


variant (nil |> v3ChildNamed "Hello") == Just 1
isVariant : Int -> UUID -> Bool

True if the given UUID is the given variant number. Always False is the integer provided is not 1 or 2!

isVariant 4 someUUID -- Always False, variant 4 doesn't exist!


someJsonValue
    |> Json.Decode.decodeValue UUID.decoder
    |> UUID.isVariant 1


isVariant 2 (someUUID |> toVariant2) -- True

Sometimes you may need to ensure that a UUID is definitely a certain version or variant. If you're decoding from JSON, you may find the following functions useful in conjunction with json-extra's fromResult function.

uuidDecoder = Json.Decode.string |> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.fromString) |> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.checkVersion 4) |> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.checkVariant 1)

checkVersion : Int -> UUID -> Result String UUID

Check which version a UUID is, and if it's not the right one, return an Err.

(nil |> v3ChildNamed "hello" |> checkVersion 4) == Err "UUID is not Version 4"


(nil |> checkVersion 1) == Err "UUID does not define a valid version"


(someVersion4UUID |> checkVersion 4) == Ok someVersion4UUID
checkVariant : Int -> UUID -> Result String UUID

Check which variant a UUID is, and if it's not the right one, return an Err.

(nil |> v3ChildNamed "hello" |> checkVariant 2) == Err "UUID is not variant 2"


(nil |> checkVariant 2) == Err "UUID does not define a valid variant"


(someVariant1UUID |> checkVariant 1) == Ok someVariant1UUID
checkNotNil : UUID -> Result String UUID

A simple function to use while chaining Results. Makes sure the UUID you're dealing with isn't the Nil UUID!

uuid =
    someString
        |> UUID.fromString
        |> Result.andThen (UUID.checkNotNil 4)