{-# LANGUAGE FlexibleInstances #-}
-- |
-- Module    : Data.ObjectID
-- Copyright : Jeremy Groven
-- License   : BSD3
--
-- Opaque type to uniquely identify things. Objects to be identified must
-- implement the 'uniqueByteString' of 'IsObject', ideally in some way that
-- different objects have different bytestrings, while two instances of the
-- same object will have the same bytestring.

module Data.ObjectID
( ObjectID
, calculateSerialize
, calculateBuilder
, calculateByteString
) where

import Crypto.Hash.Skein256 ( hash )
import Blaze.ByteString.Builder ( Builder, toByteString )
import Data.ByteString      ( ByteString )
import Data.Serialize       ( Serialize(..), encode, decode )
import Data.Serialize.Get   ( getWord64be )
import Data.Serialize.Put   ( putWord64be )
import Data.Word            ( Word64 )

-- |Opaque type used to uniquely identify objects
data ObjectID = ObjectID !Word64 !Word64 !Word64 !Word64
                deriving ( Show, Eq, Ord )

instance Serialize ObjectID where
  put (ObjectID a b c d) = do
      putWord64be a
      putWord64be b
      putWord64be c
      putWord64be d

  get = do
    a <- getWord64be
    b <- getWord64be
    c <- getWord64be
    d <- getWord64be
    return $ ObjectID a b c d

-- |Get the 'ObjectID' from a 'Serialize' instance
calculateSerialize :: Serialize a => a -> ObjectID
calculateSerialize = calculateByteString . encode

calculateBuilder :: Builder -> ObjectID
calculateBuilder = calculateByteString . toByteString

calculateByteString :: ByteString -> ObjectID
calculateByteString bs =
  let hsh = hash 256 bs
      Right oid = decode hsh
  in oid