packages feed

ipldm-1.0.0.0: ipld/IPLD/DM.hs

-- | Module      : IPLD.DM
--   Description : Provides Node and Kind types for IPLD
--   Copyright   : Zoey McBride (c) 2026
--   License     : AGPL-3.0-or-later
--   Maintainer  : zoeymcbride@mailbox.org
--   Stability   : experimental
module IPLD.DM
  ( -- * Kinds are the data types of the IPLD data model
    Kind (..),

    -- * Node objects within a DM graph
    Node,

    -- * Implementing the IPLD bytesring
    BytesData (..),

    -- * Implementing the IPLD map type
    MapData,

    -- * Implementing the IPLD list type
    ListData,

    -- * Stores raw IEEE-754 floating point data.
    FloatData (..),
  )
where

import Control.DeepSeq (NFData (..))
import Data.Array (Array)
import Data.ByteString.Builder (Builder)
import Data.ByteString.Builder qualified as Builder
import Data.Text (Text)
import Data.Word (Word16, Word32, Word64)
import MultiFormats.CID (CID)

-- | All nodes within the DM have a kind with their, and this is part of the
-- Kind sum-type, so this is just a type-alias.
type Node = Kind

-- | Implements sum type for Kind, the representable data in IPLD.
-- https://ipld.io/docs/data-model/kinds/
-- https://ipld.io/docs/data-model/node/
data Kind
  = -- | Null can only have the value `Null`.
    -- https://ipld.io/docs/data-model/kinds/#null-kind
    NullKind
  | -- | True or False.
    -- https://ipld.io/docs/data-model/kinds/#boolean-kind
    BoolKind Bool
  | -- | AKA: Integer. IPLD suggests to use an unlimited size number
    -- representation when available, so we use integer. It will be up to the
    -- implementer of DataModel whether to use this space or not.
    -- https://ipld.io/docs/data-model/kinds/#integer-kind
    -- https://ipld.io/design/tricky-choices/numeric-domain/#integers
    IntKind Integer
  | -- | AKA: Float. This is how we represent decimal numbers with Kind.
    -- https://ipld.io/docs/data-model/kinds/#float-kind
    -- https://ipld.io/design/tricky-choices/numeric-domain/#floating-point
    FloatKind FloatData
  | -- | IPLD recommends UTF-8 support, so Text is used.
    -- https://ipld.io/docs/data-model/kinds/#string-kind
    StringKind Text
  | -- | Stores arbitrary bytes of any length.
    -- https://ipld.io/docs/data-model/kinds/#bytes-kind
    BytesKind BytesData
  | -- | AKA: Link, IPLD links data together using Multiformat's Content ID/CID.
    -- Using links, we can point to data in another IPLD block.
    -- https://ipld.io/docs/data-model/kinds/#link-kind
    ResourceKind CID
  | -- | Recursive type: List of Nodes.
    -- https://ipld.io/docs/data-model/kinds/#list-kind
    ListKind (ListData Node)
  | -- | Recursive type: Map of Nodes.
    -- https://ipld.io/docs/data-model/kinds/#map-kind
    MapKind (MapData Node Node)
  deriving (Show, Eq)

-- | Fully instantiates a Kind object from lazy Haskell object.
instance NFData Kind where
  rnf NullKind = ()
  rnf (BoolKind bool) = rnf bool
  rnf (IntKind int) = rnf int
  rnf (FloatKind float) = rnf float
  rnf (BytesKind bytesdata) = rnf bytesdata
  rnf (StringKind text) = rnf text
  rnf (ResourceKind cid) = rnf cid
  rnf (ListKind list) = rnf list
  rnf (MapKind kvs) = rnf kvs

-- | New-type for `Builder` for the bytestring type IPLD in data model.
data BytesData = BytesData
  { bytesDataBuilder :: Builder,
    bytesDataLength :: Int
  }
  deriving (Show)

-- | Fully instantiates a BytesData object from lazy Haskell object.
instance NFData BytesData where
  rnf (BytesData builder len) = builder `seq` len `seq` ()

-- | Implements `Eq` on `BytesData` by executing `Builder.toLazyByteString` on
-- both inputs.
instance Eq BytesData where
  (BytesData b1 l1) == (BytesData b2 l2) =
    l1 == l2 && Builder.toLazyByteString b1 == Builder.toLazyByteString b2

-- | Type-alias for storing the map type in the IPLD data model.
type ListData t = Array Int t

-- | Type-alias for storing the map type in the IPLD data model.
type MapData k v = Array Int (k, v)

-- | FloatData stores float types for different sizes.
data FloatData
  = -- | Raw IEEE754 half precision data. This doesn't have a Haskell type.
    HalfPrecision Word16
  | -- | Raw IEEE754 single precision data. Corresponds to Haskell `Float`.
    SinglePrecision Word32
  | -- | Raw IEEE754 double precision data. Corresponds to Haskell `Double`.
    DoublePrecision Word64
  deriving (Show, Eq)

-- | Fully instantiates a FloatData object from lazy Haskell object.
instance NFData FloatData where
  rnf (HalfPrecision w16) = rnf w16 `seq` ()
  rnf (SinglePrecision w32) = rnf w32 `seq` ()
  rnf (DoublePrecision w64) = rnf w64 `seq` ()