packages feed

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

module Rattletrap.Type.PropertyValue where

import qualified Data.Foldable as Foldable
import qualified Rattletrap.ByteGet as ByteGet
import qualified Rattletrap.BytePut as BytePut
import qualified Rattletrap.Exception.UnknownProperty as UnknownProperty
import qualified Rattletrap.Schema as Schema
import qualified Rattletrap.Type.Property.Array as Property.Array
import qualified Rattletrap.Type.Property.Bool as Property.Bool
import qualified Rattletrap.Type.Property.Byte as Property.Byte
import qualified Rattletrap.Type.Property.Float as Property.Float
import qualified Rattletrap.Type.Property.Int as Property.Int
import qualified Rattletrap.Type.Property.Name as Property.Name
import qualified Rattletrap.Type.Property.QWord as Property.QWord
import qualified Rattletrap.Type.Property.Str as Property.Str
import qualified Rattletrap.Type.Property.Struct as Property.Struct
import qualified Rattletrap.Type.Str as Str
import qualified Rattletrap.Utility.Json as Json

data PropertyValue a
  = -- | Yes, a list of dictionaries. No, it doesn't make sense. These usually
    -- only have one element.
    Array (Property.Array.Array a)
  | Bool Property.Bool.Bool
  | -- | This is a strange name for essentially a key-value pair.
    Byte Property.Byte.Byte
  | Float Property.Float.Float
  | Int Property.Int.Int
  | -- | It's unclear how exactly this is different than a 'StrProperty'.
    Name Property.Name.Name
  | QWord Property.QWord.QWord
  | Str Property.Str.Str
  | Struct (Property.Struct.Struct a)
  deriving (Eq, Show)

instance (Json.FromJSON a) => Json.FromJSON (PropertyValue a) where
  parseJSON = Json.withObject "PropertyValue" $ \object ->
    Foldable.asum
      [ fmap Array $ Json.required object "array",
        fmap Bool $ Json.required object "bool",
        fmap Byte $ Json.required object "byte",
        fmap Float $ Json.required object "float",
        fmap Int $ Json.required object "int",
        fmap Name $ Json.required object "name",
        fmap QWord $ Json.required object "q_word",
        fmap Str $ Json.required object "str",
        fmap Struct $ Json.required object "struct"
      ]

instance (Json.ToJSON a) => Json.ToJSON (PropertyValue a) where
  toJSON x = case x of
    Array y -> Json.object [Json.pair "array" y]
    Bool y -> Json.object [Json.pair "bool" y]
    Byte y -> Json.object [Json.pair "byte" y]
    Float y -> Json.object [Json.pair "float" y]
    Int y -> Json.object [Json.pair "int" y]
    Name y -> Json.object [Json.pair "name" y]
    QWord y -> Json.object [Json.pair "q_word" y]
    Str y -> Json.object [Json.pair "str" y]
    Struct y -> Json.object [Json.pair "struct" y]

schema :: Schema.Schema -> Schema.Schema
schema s =
  Schema.named "property-value" . Schema.oneOf $
    fmap
      (\(k, v) -> Schema.object [(Json.pair k v, True)])
      [ ("array", Schema.ref $ Property.Array.schema s),
        ("bool", Schema.ref Property.Bool.schema),
        ("byte", Schema.ref Property.Byte.schema),
        ("float", Schema.ref Property.Float.schema),
        ("int", Schema.ref Property.Int.schema),
        ("name", Schema.ref Property.Name.schema),
        ("q_word", Schema.ref Property.QWord.schema),
        ("str", Schema.ref Property.Str.schema),
        ("struct", Schema.ref $ Property.Struct.schema s)
      ]

bytePut :: (a -> BytePut.BytePut) -> PropertyValue a -> BytePut.BytePut
bytePut putProperty value = case value of
  Array x -> Property.Array.bytePut putProperty x
  Bool x -> Property.Bool.bytePut x
  Byte x -> Property.Byte.bytePut x
  Float x -> Property.Float.bytePut x
  Int x -> Property.Int.bytePut x
  Name x -> Property.Name.bytePut x
  QWord x -> Property.QWord.bytePut x
  Str x -> Property.Str.bytePut x
  Struct x -> Property.Struct.bytePut putProperty x

byteGet :: ByteGet.ByteGet a -> Str.Str -> ByteGet.ByteGet (PropertyValue a)
byteGet getProperty kind =
  ByteGet.label "PropertyValue" $ case Str.toString kind of
    "ArrayProperty" -> fmap Array $ Property.Array.byteGet getProperty
    "BoolProperty" -> fmap Bool Property.Bool.byteGet
    "ByteProperty" -> fmap Byte Property.Byte.byteGet
    "FloatProperty" -> fmap Float Property.Float.byteGet
    "IntProperty" -> fmap Int Property.Int.byteGet
    "NameProperty" -> fmap Name Property.Name.byteGet
    "QWordProperty" -> fmap QWord Property.QWord.byteGet
    "StrProperty" -> fmap Str Property.Str.byteGet
    "StructProperty" -> fmap Struct $ Property.Struct.byteGet getProperty
    x -> ByteGet.throw $ UnknownProperty.UnknownProperty x