packages feed

positron-0.1.0.0: library/Positron/UUID.hs

module Positron.UUID
    ( UUID(..)
    , fromByteString
    , unsafeFromByteString
    , fromText
    ) where

import Positron.Import

-- external modules

import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as B
import qualified Data.Text as T
import qualified Data.Text.Encoding as T

-- local modules

import Positron.Util

data UUID = UUID
    { uuidByteString :: ByteString
    , uuidText :: Text
    }
    deriving Eq

instance Show UUID where
    show u = T.unpack $ "UUID <" <> uuidText u <> ">"

fromByteString :: ByteString -> Maybe UUID
fromByteString b
    | B.length b == 16 = Just $ UUID b text
    | otherwise = Nothing
  where
    text = T.decodeLatin1 $
        fold [hex1, "-", hex2, "-", hex3, "-", hex4, "-", hex5]
    hex1 = B.take 8 hexed
    hex2 = B.take 4 $ B.drop 8 hexed
    hex3 = B.take 4 $ B.drop 12 hexed
    hex4 = B.take 4 $ B.drop 16 hexed
    hex5 = B.drop 20 hexed
    hexed = toByteString $ B.byteStringHex b

unsafeFromByteString :: ByteString -> UUID
unsafeFromByteString = fromMaybe err .  fromByteString
  where
    err = error "UUID ByteString is not 16 bytes long"

fromText :: Text -> Maybe UUID
fromText t
    | T.length t /= 36 = Nothing
    | not (B.all isValidHexWord8 packed) = Nothing
    | otherwise = Just $ UUID (B.pack $ forPairs (B.unpack packed) unhex) t
  where
    rawBytes = T.encodeUtf8 t
    packed = fold [hex1, hex2, hex3, hex4, hex5]
    hex1 = B.take 8 rawBytes
    hex2 = B.take 4 $ B.drop (8 + 1) rawBytes
    hex3 = B.take 4 $ B.drop (8 + 1 + 4 + 1) rawBytes
    hex4 = B.take 4 $ B.drop (8 + 1 + 4 + 1 + 4 + 1) rawBytes
    hex5 = B.drop (8 + 1 + 4 + 1 + 4 + 1 + 4 + 1) rawBytes
    isValidHexWord8 w = (0x29 < w && w < 0x40) || (0x60 < w && w < 0x67)

unhex :: Word8 -> Word8 -> Word8
unhex upper lower = (trueValue upper `shiftL` 4) .|. trueValue lower
  where
    trueValue x
        | x < 0x40 = x - 0x30 -- x is a digit; subtract the ord of 0
        | otherwise = 0xa + x - 0x61 -- x is an alphabet; subtract the ord of a

forPairs :: [a] -> (a -> a -> b) -> [b]
forPairs [] _ = []
forPairs [_] _ = []
forPairs (x : y : xs) f = f x y : forPairs xs f