{-# 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