module Cachix.Api.Signing
  ( fingerprint
  , passthroughSizeSink
  , passthroughHashSink
  ) where

import           Crypto.Hash
import           Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.ByteArray                as BA
import qualified Data.ByteString as BS
import           Data.ByteString (ByteString)
import qualified Data.ByteString.Base16        as B16
import           Data.Conduit
import qualified Data.Conduit.Combinators      as CC
import           Data.IORef
import           Data.String.Conv (toS)
import qualified Data.Text as T
import           Data.Text (Text)


-- perl/lib/Nix/Manifest.pm:fingerprintPath
-- NB: references must be sorted
fingerprint :: Text -> Text -> Integer -> [Text] -> ByteString
fingerprint storePath narHash narSize references = toS $ T.intercalate ";"
  ["1", storePath, narHash, T.pack (show narSize), T.intercalate "," references]

-- Useful sinks for streaming nars

sizeSink :: MonadIO m => ConduitT ByteString o m Integer
sizeSink = CC.foldM (\p n -> return (p + fromIntegral (BS.length n))) 0

hashSink :: MonadIO m => ConduitT ByteString o m (Context SHA256)
hashSink = CC.foldM (\p n -> return (hashUpdate p n)) hashInit

passthroughSizeSink :: MonadIO m => IORef Integer -> ConduitT ByteString ByteString m ()
passthroughSizeSink ioref = passthroughSink sizeSink (liftIO . writeIORef ioref)

passthroughHashSink :: MonadIO m => IORef Text -> ConduitT ByteString ByteString m ()
passthroughHashSink ioref = passthroughSink hashSink (liftIO . writeIORef ioref . transf)
  where
    -- TODO: use cryptonite B16 to get rid of extra dep and simplify
    transf = toS . B16.encode . BS.pack . BA.unpack . hashFinalize