module ContentStore.Object ( Domain(..) , ObjectHash , StorePath(..) , hashObject , hashToText , textToHashBytes , shardForHash ) where import Crypto.Hash (Digest, SHA256, hash) import Data.ByteArray (convert) import Data.ByteString.Base16 (decode, encode) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) import qualified Data.ByteString as BS import qualified Data.Text as T newtype Domain = Domain { unDomain :: Text } deriving (Eq, Ord, Show) type ObjectHash = Text newtype StorePath = StorePath { unStorePath :: FilePath } deriving (Eq, Ord, Show) hashObject :: Domain -> BS.ByteString -> ObjectHash hashObject (Domain domain) payload = hashToText digest where digest :: Digest SHA256 digest = hash (encodeUtf8 domain <> BS.pack [0x00] <> payload) hashToText :: Digest SHA256 -> Text hashToText = decodeUtf8 . encode . (convert :: Digest SHA256 -> BS.ByteString) textToHashBytes :: Text -> Either String BS.ByteString textToHashBytes h = case decode (encodeUtf8 h) of Left _ -> Left "invalid hexadecimal hash" Right raw | BS.length raw == 32 -> Right raw | otherwise -> Left "hash must decode to 32 bytes" shardForHash :: ObjectHash -> FilePath shardForHash = T.unpack . T.take 3