packages feed

qute-cli-0.1.0: src/Data/KTest.hs

-- SPDX-FileCopyrightText: 2026 Sören Tempel <soeren+git@soeren-tempel.net>
--
-- SPDX-License-Identifier: GPL-3.0-only

module Data.KTest
  ( KTest (..),
    KTestObj (..),
    fromAssign,
  )
where

import Control.Monad (forM_, void, when)
import Data.Binary (Binary (get, put))
import Data.Binary.Get (getLazyByteString, getWord32be)
import Data.Binary.Put (putLazyByteString, putWord32be)
import Data.ByteString.Lazy qualified as BL
import Data.Map qualified as Map
import Data.String (fromString)
import Data.Word (Word32)
import Language.QBE.Backend.Store (Assign)
import Language.QBE.Simulator.Default.Expression qualified as DE
import Language.QBE.Simulator.Memory (toBytes)

newtype KTestString
  = KTestString BL.ByteString
  deriving (Show, Eq)

instance Binary KTestString where
  put (KTestString bs) =
    putWord32be (fromIntegral $ BL.length bs) >> putLazyByteString bs

  get = do
    len <- getWord32be
    str <- getLazyByteString (fromIntegral len)
    pure $ KTestString str

------------------------------------------------------------------------

data KTestObj
  = KTestObj
  { objName :: BL.ByteString,
    objBytes :: BL.ByteString
  }
  deriving (Show, Eq)

instance Binary KTestObj where
  put (KTestObj name bytes) = do
    put (KTestString name)
    put (KTestString bytes)

  get = do
    (KTestString name) <- get
    (KTestString bytes) <- get
    pure $ KTestObj name bytes

fromAssign :: Assign -> [KTestObj]
fromAssign assign = map go $ Map.toList assign
  where
    go :: (String, DE.RegVal) -> KTestObj
    go (name, value) =
      KTestObj (fromString name) (BL.pack $ toBytes value)

------------------------------------------------------------------------

data KTest
  = KTest
  { ktArgs :: [BL.ByteString],
    ktObjs :: [KTestObj]
  }
  deriving (Show, Eq)

header :: BL.ByteString
header = fromString "KTEST"

legacyHeader :: BL.ByteString
legacyHeader = fromString "BOUT\n"

version :: Word32
version = 3

instance Binary KTest where
  get = do
    hdr <- getLazyByteString (BL.length header)
    when (hdr /= header && hdr /= legacyHeader) $
      fail "invalid ktest header"
    ver <- getWord32be
    when (ver > version) $
      fail "unsupported ktest version"

    numArgs <- getWord32be
    strs <-
      mapM
        ( \_ -> do
            (KTestString s) <- get
            pure s
        )
        [1 .. numArgs]

    when (ver >= 2) $
      -- XXX: Skip symArgvs and symArgvLen for now.
      void (getWord32be >> getWord32be)

    numObjs <- getWord32be
    objs <- mapM (const get) [1 .. numObjs]

    pure $ KTest strs objs

  put (KTest args objs) = do
    putLazyByteString header
    putWord32be version

    putWord32be (fromIntegral $ length args)
    forM_ args (put . KTestString)

    -- XXX: Skip symArgvs and symArgvLen for now.
    putWord32be 0
    putWord32be 0

    putWord32be (fromIntegral $ length objs)
    forM_ objs put