packages feed

clickhouse-migrate-0.1.0.0: src/ClickHouseMigrate/Internal/Checksum.hs

-- | Versioned byte encoder. Keep in agreement with docs/user/contracts/checksum-v1.md.
module ClickHouseMigrate.Internal.Checksum
  ( completionPolicyV1,
    encodeStepV1,
    encodeMigrationV1,
    digest,
    digestHex,
  )
where

import ClickHouseMigrate.Prelude
import Crypto.Hash.SHA256 qualified as SHA256
import Data.ByteString qualified as BS
import Data.ByteString.Builder (Builder, byteString, toLazyByteString, word64BE)
import Data.ByteString.Lazy qualified as Lazy
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text
import Numeric (showHex)

completionPolicyV1 :: ByteString
completionPolicyV1 = "clickhouse-migrate/local-sync-v1;async_insert=0;mutations_sync=1;alter_sync=1"

field :: ByteString -> Builder
field bs = word64BE (fromIntegral (BS.length bs)) <> byteString bs

encodeStepV1 :: Text -> Text -> Text -> ByteString -> ByteString
encodeStepV1 component migration step sql =
  Lazy.toStrict $
    toLazyByteString $
      byteString "clickhouse-migrate/step\0v1\0"
        <> field completionPolicyV1
        <> foldMap (field . Text.encodeUtf8) [component, migration, step]
        <> field sql

encodeMigrationV1 :: Text -> Text -> [ByteString] -> ByteString
encodeMigrationV1 component migration steps =
  Lazy.toStrict $
    toLazyByteString $
      byteString "clickhouse-migrate/migration\0v1\0"
        <> field completionPolicyV1
        <> foldMap (field . Text.encodeUtf8) [component, migration]
        <> word64BE (fromIntegral (length steps))
        <> foldMap field steps

digest :: ByteString -> ByteString
digest = SHA256.hash

digestHex :: ByteString -> Text
digestHex = Text.pack . concatMap hex . BS.unpack
  where
    hex byte = case showHex byte "" of
      [c] -> ['0', c]
      cs -> cs