packages feed

rattletrap-14.1.4: src/lib/Rattletrap/Type/Dictionary.hs

module Rattletrap.Type.Dictionary where

import qualified Data.Text as Text
import qualified Rattletrap.ByteGet as ByteGet
import qualified Rattletrap.BytePut as BytePut
import qualified Rattletrap.Schema as Schema
import qualified Rattletrap.Type.List as RList
import qualified Rattletrap.Type.Str as Str
import qualified Rattletrap.Utility.Json as Json

data Dictionary a = Dictionary
  { elements :: RList.List (Str.Str, a),
    lastKey :: Str.Str
  }
  deriving (Eq, Show)

instance (Json.FromJSON a) => Json.FromJSON (Dictionary a) where
  parseJSON = Json.withObject "Dictionary" $ \o -> do
    elements <- Json.required o "elements"
    lastKey <- Json.required o "last_key"
    pure Dictionary {elements = elements, lastKey = lastKey}

instance (Json.ToJSON a) => Json.ToJSON (Dictionary a) where
  toJSON x =
    Json.object
      [ Json.pair "elements" . RList.toList $ elements x,
        Json.pair "last_key" $ lastKey x
      ]

schema :: Schema.Schema -> Schema.Schema
schema s =
  Schema.named ("dictionary-" <> Text.unpack (Schema.name s)) $
    Schema.object
      [ (Json.pair "elements" . Schema.json . Schema.array $ elementSchema s, True),
        (Json.pair "last_key" $ Schema.ref Str.schema, True)
      ]

elementSchema :: Schema.Schema -> Schema.Schema
elementSchema s =
  Schema.named ("dictionary-element-" <> Text.unpack (Schema.name s)) $
    Schema.tuple
      [ Schema.ref Str.schema,
        Schema.ref s
      ]

lookup :: Str.Str -> Dictionary a -> Maybe a
lookup k = Prelude.lookup k . RList.toList . elements

bytePut :: (a -> BytePut.BytePut) -> Dictionary a -> BytePut.BytePut
bytePut f x =
  foldMap (\(k, v) -> Str.bytePut k <> f v) (RList.toList $ elements x)
    <> Str.bytePut (lastKey x)

byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (Dictionary a)
byteGet = ByteGet.label "Dictionary" . byteGetWith 0 []

byteGetWith ::
  Int ->
  [(Int, (Str.Str, a))] ->
  ByteGet.ByteGet a ->
  ByteGet.ByteGet (Dictionary a)
byteGetWith i xs f = do
  k <- ByteGet.label ("key (" <> show i <> ")") Str.byteGet
  if isNone k
    then
      pure
        Dictionary
          { elements = RList.fromList . reverse $ fmap snd xs,
            lastKey = k
          }
    else do
      v <- ByteGet.label ("value (" <> Str.toString k <> ")") f
      byteGetWith (i + 1) ((i, (k, v)) : xs) f

isNone :: Str.Str -> Bool
isNone = (== Text.pack "None") . Text.filter (/= '\x00') . Str.toText