mfmts-1.0.0.0: cid/MultiFormats/CID/Internal.hs
-- | Module : MultiFormats.CID.Internal
-- Description : Internal implementation for CID
-- Copyright : Zoey McBride (c) 2026
-- License : AGPL-3.0-or-later
-- Maintainer : zoeymcbride@mailbox.org
-- Stability : experimental
--
-- This file provides implemtations for serialization and extraction for
-- versioned CID data (when we know ahead-of-time to be CIDv0 or CIDv1).
module MultiFormats.CID.Internal
( CIDData (CIDData, cidVersion, cidDataCodec, cidHashCodec, cidHashDigest),
-- * Extraction over CIDVersion
extractCIDv1,
extractCIDv0,
-- * Serialization over CIDVersion
serializeCIDv1,
serializeCIDv0,
-- * Constructs CIDv0 from ByteString
mkCIDv0,
)
where
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.ByteString qualified as Bytes
import Data.Maybe (fromJust)
import MultiFormats.CID.Errors
import MultiFormats.CID.Extractor
import MultiFormats.CID.Internal.CIDCodec
import MultiFormats.CID.Internal.CIDHash
import MultiFormats.CID.Internal.CIDVersion
import MultiFormats.CID.Serializer
import MultiFormats.MultiCodec (MultiCodec, resolveCodecValue)
import MultiFormats.MultiHash (MultiHash, resolveHashName)
import MultiFormats.VarInt (VarInt)
-- | Define the CID as a constructor of CIDData
data CIDData = CIDData
{ -- | Specifies the version (CIDv0 or CIDv1) of this CID.
cidVersion :: VersionEnum,
-- | Describes the content type of the hashed data.
cidDataCodec :: MultiCodec,
-- | Contains the hash functoin used.
cidHashCodec :: MultiHash,
-- | Stores the hash function's output.
cidHashDigest :: ByteString
}
deriving (Eq)
-- | Applies the extraction after version# for CIDv0 was extracted.
extractCIDv0 :: Extraction CIDData
extractCIDv0 ondata = first mkCIDv0 <$> splitDigestCIDv0 ondata
where
-- Get the hash digest from ByteString if there is enough bytes.
splitDigestCIDv0 digest
| Bytes.length digest < hashlen = Left (InvalidHash IncorrectLength)
| otherwise = Right (Bytes.splitAt hashlen digest)
where
hashlen = cidv0HashLength
-- | Applies the extraction after version# for CIDv1 was extracted.
extractCIDv1 :: Extraction CIDData
extractCIDv1 oncodec = do
-- Extract the 1st MultiCodec from the bytes.
(CIDCodec multicodec, onhash) <- extractor oncodec
-- Extract the 2nd MultiCodec and get CIDHash
(CIDHash multihash hashdigest, stop) <- extractor onhash
-- return the CIDv1 data with the trailing bytes.
return (CIDData CIDv1 multicodec multihash hashdigest, stop)
-- | Serializes CIDv0 by adding the Codec Byte to its digest
serializeCIDv0 :: Serialization CIDData
serializeCIDv0 ciddata =
return $ Bytes.cons cidv0Byte1st (cidHashDigest ciddata)
-- | Serializes CIDv1 into VarInts of its components.
serializeCIDv1 :: Serialization CIDData
serializeCIDv1 ciddata =
let digest = cidHashDigest ciddata
digestlength = Bytes.length digest
in Bytes.concat
<$> sequence
[ serializer cidv1VarInt1st,
serializer (cidDataCodec ciddata),
serializer (cidHashCodec ciddata),
serializer (fromIntegral digestlength :: VarInt),
pure digest
]
-- | Provides the contructor for CIDv0s, see:
-- https://github.com/multiformats/cid?tab=readme-ov-file#cidv0
mkCIDv0 :: ByteString -> CIDData
mkCIDv0 digest =
CIDData
{ cidVersion = CIDv0,
cidDataCodec = codec "dag-pb",
cidHashCodec = hash "sha2-256",
cidHashDigest = digest
}
where
-- Forces a resolve from MultiCodec
codec str = fromJust $ resolveCodecValue str
-- Forces a resolve from MultiHash
hash str = fromJust $ resolveHashName str