packages feed

mfmts-1.0.0.0: cid/MultiFormats/CID.hs

-- | Module      : MultiFormats.CID
--   Description : Implements multiformats CID
--   Copyright   : Zoey McBride (c) 2026
--   License     : AGPL-3.0-or-later
--   Maintainer  : zoeymcbride@mailbox.org
--   Stability   : experimental
--
-- This module provides a way for extracting, parsing and serializing CID data.
-- See: https://github.com/multiformats/cid
module MultiFormats.CID
  ( CID (CID),
    getDataCodec,
    getHashCodec,
    getHashDigest,
    getVersion,
  )
where

import Data.BaseSystem (encoder)
import Data.BaseSystems (base58btc)
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.ByteString qualified as Bytes
import MultiFormats.CID.Errors
import MultiFormats.CID.Extractor
import MultiFormats.CID.Internal
import MultiFormats.CID.Internal.CIDDecoder
import MultiFormats.CID.Internal.CIDVersion
import MultiFormats.CID.Parser
import MultiFormats.CID.Serializer
import MultiFormats.MultiCodec (MultiCodec)
import MultiFormats.MultiHash (MultiHash)
import Text.Printf (printf)

-- | Wraps CIDData to implement multi-version Parser, Extractor & Serializer.
newtype CID = CID CIDData deriving (Eq)

-- | Repl/Unit test friendly Show.
instance Show CID where
  show cid =
    let version = getVersion cid
        datacodec = getDataCodec cid
        hashcodec = getHashCodec cid
        hashdigest = getHashDigest cid
     in printf
          "%s(DataCodec=%s, HashCodec=%s, HashDigest[%s]=%s)"
          (show version)
          (show datacodec)
          (show hashcodec)
          (show showbase)
          (encoder showbase hashdigest)
    where
      -- Sets the base implementation to show the hash digest with.
      showbase = base58btc

-- | Parses a CIDv1 from a MultiBase encoded string OR a CIDv0 from a base58btc
-- encoded string. Fails when there is extra bytes past the CID.
instance Parser CID where
  parser cidstr = do
    -- Decode to bytes based on the String's encoding.
    CIDDecoder cidbytes <- parser cidstr
    -- Extracts the CID's data from bytes.
    (cid, trailing) <- extractor cidbytes
    -- Fail if there is any leftovers.
    if Bytes.length trailing > 0
      then Left (InvalidData TrailingBytes)
      else Right cid

-- | Extracts a CID from a ByteString. Allows trailing bytes.
instance Extractor CID where
  extractor bytes = do
    -- First, extract the version from bytes
    (CIDVersion version, rest) <- extractor bytes
    -- Apply the constructor if the CID was successfully extracted from bytes
    first CID <$> case version of
      CIDv0 -> extractCIDv0 rest
      CIDv1 -> extractCIDv1 rest

-- | Serializes a CID from representation.
instance Serializer CID where
  serializer (CID ciddata) =
    case cidVersion ciddata of
      CIDv0 -> serializeCIDv0 ciddata
      CIDv1 -> serializeCIDv1 ciddata

-- | Gets VersionEnum from a CID.
getVersion :: CID -> VersionEnum
getVersion (CID ciddata) = cidVersion ciddata

-- | Gets data-type MultiCodec from a CID.
getDataCodec :: CID -> MultiCodec
getDataCodec (CID ciddata) = cidDataCodec ciddata

-- | Gets MultiHash codec from a CID.
getHashCodec :: CID -> MultiHash
getHashCodec (CID ciddata) = cidHashCodec ciddata

-- | Gets the hash digest from a CID.
getHashDigest :: CID -> ByteString
getHashDigest (CID ciddata) = cidHashDigest ciddata