packages feed

amazonka-dynamodb-2.0: src/Amazonka/DynamoDB/Types/AttributeValue.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# OPTIONS_GHC -Wall -Werror #-}

-- |
-- Module      : Amazonka.DynamoDB.AttributeValue
-- Copyright   : (c) 2013-2023 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka.com>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
module Amazonka.DynamoDB.Types.AttributeValue where

import Amazonka.Data
import Amazonka.Prelude
import Data.Aeson (pairs)
import Data.Hashable
import Data.Map (Map)
import Data.Vector (Vector)

#if MIN_VERSION_aeson(2,0,0)
import qualified  Data.Aeson.KeyMap as KeyMap
#else
import qualified  Data.HashMap.Strict as KeyMap
#endif

-- | Represents the data for an attribute.
--
-- DynamoDB sends and receives JSON objects which contain a single
-- item whose key is a data type and the value is the data itself. We
-- provide an actual sum type to interact with these.
--
-- For more information, see
-- <https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes Data Types>
-- in the /Amazon DynamoDB Developer Guide/.
data AttributeValue
  = -- | An attribute of type List. For example:
    --
    -- @\"L\": [{\"S\": \"Cookies\"} , {\"S\": \"Coffee\"}, {\"N\", \"3.14159\"}]@
    L (Vector AttributeValue)
  | -- | An attribute of type Number Set. For example:
    --
    -- @\"NS\": [\"42.2\", \"-19\", \"7.5\", \"3.14\"]@
    --
    -- Numbers are sent across the network to DynamoDB as strings, to maximize
    -- compatibility across languages and libraries. However, DynamoDB treats
    -- them as number type attributes for mathematical operations.
    NS (Vector Text)
  | -- | An attribute of type Map. For example:
    --
    -- @\"M\": {\"Name\": {\"S\": \"Joe\"}, \"Age\": {\"N\": \"35\"}}@
    M (Map Text AttributeValue)
  | -- | An attribute of type Null. For example:
    --
    -- @\"NULL\": true@
    NULL
  | -- | An attribute of type Number. For example:
    --
    -- @\"N\": \"123.45\"@
    --
    -- Numbers are sent across the network to DynamoDB as strings, to maximize
    -- compatibility across languages and libraries. However, DynamoDB treats
    -- them as number type attributes for mathematical operations.
    N Text
  | -- | An attribute of type Binary Set. For example:
    --
    -- @\"BS\": [\"U3Vubnk=\", \"UmFpbnk=\", \"U25vd3k=\"]@
    BS (Vector Base64)
  | -- | An attribute of type Binary. For example:
    --
    -- @\"B\": \"dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk\"@
    B Base64
  | -- | An attribute of type String Set. For example:
    --
    -- @\"SS\": [\"Giraffe\", \"Hippo\" ,\"Zebra\"]@
    SS (Vector Text)
  | -- | An attribute of type String. For example:
    --
    -- @\"S\": \"Hello\"@
    S Text
  | -- | An attribute of type Boolean. For example:
    --
    -- @\"BOOL\": true@
    BOOL Bool
  deriving stock (Eq, Read, Show, Generic)
  deriving anyclass (NFData)

instance Hashable AttributeValue where
  hashWithSalt salt = \case
    L avs -> salt `hashWithSalt` (0 :: Int) `hashVector` avs
    NS ns -> salt `hashWithSalt` (1 :: Int) `hashVector` ns
    M m -> salt `hashWithSalt` (2 :: Int) `hashWithSalt` m
    NULL -> salt `hashWithSalt` (3 :: Int) `hashWithSalt` ()
    N n -> salt `hashWithSalt` (4 :: Int) `hashWithSalt` n
    BS bs -> salt `hashWithSalt` (5 :: Int) `hashVector` bs
    B b -> salt `hashWithSalt` (6 :: Int) `hashWithSalt` b
    SS ss -> salt `hashWithSalt` (7 :: Int) `hashVector` ss
    S s -> salt `hashWithSalt` (8 :: Int) `hashWithSalt` s
    BOOL b -> salt `hashWithSalt` (9 :: Int) `hashWithSalt` b
    where
      hashVector :: Hashable a => Int -> Vector a -> Int
      hashVector = hashUsing toList

instance FromJSON AttributeValue where
  parseJSON = withObject "AttributeValue" $ \o ->
    case KeyMap.toList o of
      [("L", v)] -> L <$> parseJSON v
      [("NS", v)] -> NS <$> parseJSON v
      [("M", v)] -> M <$> parseJSON v
      [("NULL", _)] -> pure NULL
      [("N", v)] -> N <$> parseJSON v
      [("BS", v)] -> BS <$> parseJSON v
      [("B", v)] -> B <$> parseJSON v
      [("SS", v)] -> SS <$> parseJSON v
      [("S", v)] -> S <$> parseJSON v
      [("BOOL", v)] -> BOOL <$> parseJSON v
      [] -> fail "No keys"
      _ -> fail $ "Multiple or unrecognized keys: " ++ show (KeyMap.keys o)

instance ToJSON AttributeValue where
  toJSON =
    object . pure . \case
      L avs -> "L" .= avs
      NS ns -> "NS" .= ns
      M m -> "M" .= m
      NULL -> "NULL" .= True
      N n -> "N" .= n
      BS bs -> "BS" .= bs
      B b -> "B" .= b
      SS ss -> "SS" .= ss
      S s -> "S" .= s
      BOOL b -> "BOOL" .= b
  toEncoding =
    pairs . \case
      L avs -> "L" .= avs
      NS ns -> "NS" .= ns
      M m -> "M" .= m
      NULL -> "NULL" .= True
      N n -> "N" .= n
      BS bs -> "BS" .= bs
      B b -> "B" .= b
      SS ss -> "SS" .= ss
      S s -> "S" .= s
      BOOL b -> "BOOL" .= b