module StoreChunks (storeChunks) where import Prelude () import BasicPrelude import Crypto.Hash (Digest, SHA1, SHA256, SHA512, hashUpdate, hashFinalize, hashInit) import System.Directory (createDirectoryIfMissing, renameFile, createFileLink, doesFileExist) import UnexceptionalIO (Unexceptional, UIO) import qualified UnexceptionalIO as UIO import qualified Data.ByteString as ByteString import qualified Data.IPLD.CID as CID import qualified Data.Multihash as Multihash digestCID :: (Multihash.Multihashable a) => Digest a -> Text digestCID = CID.cidToText . CID.newCidV1 CID.Raw createLinkIfNotExist :: FilePath -> FilePath -> IO () createLinkIfNotExist target link = do exist <- doesFileExist link when (not exist) $ createFileLink target link storeChunks :: (Unexceptional m) => FilePath -> String -> UIO (Maybe ByteString) -> m ( Either IOError (FilePath, Digest SHA1, Digest SHA256, Digest SHA512) ) storeChunks storePath tmpName getChunk = loop hashInit hashInit hashInit where tmpPath = storePath ++ "/tmp/" ++ tmpName cidPath digest = storePath ++ "/" ++ textToString (digestCID digest) loop sha1 sha256 sha512 = do Just chunk <- UIO.lift getChunk if ByteString.null chunk then finish sha1 sha256 sha512 else step sha1 sha256 sha512 chunk step sha1 sha256 sha512 chunk = do result <- UIO.fromIO' (error.show) $ do createDirectoryIfMissing True (storePath ++ "/tmp") ByteString.appendFile tmpPath chunk case result of Left e -> return (Left e) Right () -> loop (hashUpdate sha1 chunk) (hashUpdate sha256 chunk) (hashUpdate sha512 chunk) finish sha1 sha256 sha512 = do let sha1f = hashFinalize sha1 let sha256f = hashFinalize sha256 let sha512f = hashFinalize sha512 let finalPath = cidPath sha512f let finalCID = textToString (digestCID sha512f) (fmap.fmap) (const (finalPath, sha1f, sha256f, sha512f)) $ UIO.fromIO' (error.show) $ do renameFile tmpPath finalPath createLinkIfNotExist finalCID (cidPath sha1f) createLinkIfNotExist finalCID (cidPath sha256f)