{-# LANGUAGE OverloadedStrings, TemplateHaskell, DeriveDataTypeable, TypeFamilies, GeneralizedNewtypeDeriving #-}
module Keys.UUID where
import Prelude
import Data.Bits
import Data.Char
import Data.UUID (fromString, toString, toWords, fromWords)
import qualified Data.UUID as U (UUID(..), nil)
import Data.UUID.V4(nextRandom)
import Control.Applicative ((<$>))
-- import Data.Aeson.Types (FromJSON(..), ToJSON(..), typeMismatch)
-- import qualified Data.Aeson.Types as Aeson (Value(String))
-- import Data.Text.Encoding (encodeUtf8, decodeUtf8)
-- import Control.Monad(mzero)
import Data.Data
import Data.Hashable
import Control.Monad
import Data.Text (unpack, pack, Text)
import qualified Data.Text as T
import Keys.Random
import Data.SafeCopy
import Data.Serialize
import Web.PathPieces
import Foreign.Storable
import Text.Blaze
import Data.Word
import Data.Aeson
import qualified Data.Aeson as Aeson
newtype UUID = UUID U.UUID
deriving(Ord, Eq, Data, Typeable, Storable)
instance Show UUID where
show = strUUID
instance Read UUID where
readsPrec x y = map (\(a,b) -> (UUID a, b)) $ readsPrec x y
unUUID :: UUID -> U.UUID
unUUID (UUID u) = u
strUUID :: UUID -> String
strUUID (UUID u) = toString u
txtUUID :: UUID -> Text
txtUUID (UUID u) = uuidToText (toWords u)
where
uuidToText :: (Word32, Word32, Word32, Word32) -> Text
uuidToText (w0, w1, w2, w3) = hexw w0 $ hexw' w1 $ hexw' w2 $ hexw w3 T.empty
hexw :: Word32 -> Text -> Text
hexw w s = hexn w 28 `T.cons` hexn w 24 `T.cons` hexn w 20 `T.cons` hexn w 16
`T.cons` hexn w 12 `T.cons` hexn w 8 `T.cons` hexn w 4 `T.cons` hexn w 0 `T.cons` s
hexw' :: Word32 -> Text -> Text
hexw' w s = '-' `T.cons` hexn w 28 `T.cons` hexn w 24 `T.cons` hexn w 20 `T.cons` hexn w 16
`T.cons` '-' `T.cons` hexn w 12 `T.cons` hexn w 8 `T.cons` hexn w 4 `T.cons` hexn w 0 `T.cons` s
hexn :: Word32 -> Int -> Char
hexn w r = intToDigit $ fromIntegral ((w `shiftR` r) .&. 0xf)
nil :: UUID
nil = UUID U.nil
instance Serialize UUID where
put (UUID u) = put $ toWords u
get = (UUID . (\(w1,w2,w3,w4) -> fromWords w1 w2 w3 w4)) `fmap` get
instance HasRandom UUID where
rnd = UUID `fmap` nextRandom
instance ToMarkup UUID where
toMarkup u = toMarkup $ pack $ strUUID u
instance ToJSON UUID where
toJSON u = Aeson.String (txtUUID u)
instance FromJSON UUID where
parseJSON (Aeson.String s) = case fromString (T.unpack s) of
Just u -> return $ UUID u
Nothing -> mzero
parseJSON _ = mzero
{- these can be useful in postgresql-simple:
instance FromField UUID where
fromField f bs =
case bs of
Nothing -> returnError UnexpectedNull f ""
-- Parse the string values in the enum of sql/tableAuth.sql
Just s -> case fromString . unpack . decodeUtf8 $ s of
Just uuid -> pure $ UUID $ uuid
Nothing -> returnError ConversionFailed f $
"UUID must be a 16 bytes long ByteString in network byte order. Input UUID was: " ++
(unpack (decodeUtf8 s))
instance ToField UUID where
toField (UUID u) = Escape . encodeUtf8 . pack . toString $ u
-}
instance Hashable UUID where
hashWithSalt i (UUID u) = hashWithSalt i (toWords u)
instance SafeCopy UUID where
putCopy (UUID u) = contain $ safePut $ toWords u
getCopy = contain $ (UUID . (\(a,b,c,d) -> fromWords a b c d) <$> safeGet)
instance PathPiece UUID where
-- fromPathPiece :: Text -> Maybe s
fromPathPiece t = UUID <$> (fromString $ unpack t)
-- toPathPiece :: s -> Text
toPathPiece (UUID u) = pack $ toString u