module Tendermint.SDK.Codec
  ( HasCodec(..)
  , defaultSDKAesonOptions
  ) where

import           Data.Aeson                    (Options)
import           Data.Aeson.Casing             (aesonDrop, snakeCase)
import           Data.Bifunctor                (first)
import qualified Data.ByteString               as BS
import           Data.Int                      (Int32, Int64)
import qualified Data.ProtoLens.Encoding.Bytes as PB
import           Data.String.Conversions       (cs)
import           Data.Text                     (Text)
import           Data.Word                     (Word32, Word64)



-- | This class is used as a codec for all items stored in
-- | the database as well as incoming transaction messages.
class HasCodec a where
    encode :: a -> BS.ByteString
    decode :: BS.ByteString -> Either Text a

instance HasCodec () where
  encode :: () -> ByteString
encode = ByteString -> () -> ByteString
forall a b. a -> b -> a
const ""
  decode :: ByteString -> Either Text ()
decode = Either Text () -> ByteString -> Either Text ()
forall a b. a -> b -> a
const (Either Text () -> ByteString -> Either Text ())
-> Either Text () -> ByteString -> Either Text ()
forall a b. (a -> b) -> a -> b
$ () -> Either Text ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()


instance HasCodec Word32 where
  encode :: Word32 -> ByteString
encode = Builder -> ByteString
PB.runBuilder (Builder -> ByteString)
-> (Word32 -> Builder) -> Word32 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Builder
PB.putFixed32
  decode :: ByteString -> Either Text Word32
decode = (String -> Text) -> Either String Word32 -> Either Text Word32
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Either String Word32 -> Either Text Word32)
-> (ByteString -> Either String Word32)
-> ByteString
-> Either Text Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Word32 -> ByteString -> Either String Word32
forall a. Parser a -> ByteString -> Either String a
PB.runParser Parser Word32
PB.getFixed32

instance HasCodec Int32 where
  encode :: Int32 -> ByteString
encode = Builder -> ByteString
PB.runBuilder (Builder -> ByteString)
-> (Int32 -> Builder) -> Int32 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Builder
PB.putFixed32 (Word32 -> Builder) -> (Int32 -> Word32) -> Int32 -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> Word32
PB.signedInt32ToWord
  decode :: ByteString -> Either Text Int32
decode = (String -> Text) -> Either String Int32 -> Either Text Int32
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Either String Int32 -> Either Text Int32)
-> (ByteString -> Either String Int32)
-> ByteString
-> Either Text Int32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Int32 -> ByteString -> Either String Int32
forall a. Parser a -> ByteString -> Either String a
PB.runParser (Word32 -> Int32
PB.wordToSignedInt32 (Word32 -> Int32) -> Parser Word32 -> Parser Int32
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Word32
PB.getFixed32)

instance HasCodec Word64 where
  encode :: Word64 -> ByteString
encode = Builder -> ByteString
PB.runBuilder (Builder -> ByteString)
-> (Word64 -> Builder) -> Word64 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Builder
PB.putFixed64
  decode :: ByteString -> Either Text Word64
decode = (String -> Text) -> Either String Word64 -> Either Text Word64
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Either String Word64 -> Either Text Word64)
-> (ByteString -> Either String Word64)
-> ByteString
-> Either Text Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Word64 -> ByteString -> Either String Word64
forall a. Parser a -> ByteString -> Either String a
PB.runParser Parser Word64
PB.getFixed64

instance HasCodec Int64 where
  encode :: Int64 -> ByteString
encode = Builder -> ByteString
PB.runBuilder (Builder -> ByteString)
-> (Int64 -> Builder) -> Int64 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Builder
PB.putFixed64 (Word64 -> Builder) -> (Int64 -> Word64) -> Int64 -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Word64
PB.signedInt64ToWord
  decode :: ByteString -> Either Text Int64
decode = (String -> Text) -> Either String Int64 -> Either Text Int64
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (Either String Int64 -> Either Text Int64)
-> (ByteString -> Either String Int64)
-> ByteString
-> Either Text Int64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parser Int64 -> ByteString -> Either String Int64
forall a. Parser a -> ByteString -> Either String a
PB.runParser (Word64 -> Int64
PB.wordToSignedInt64 (Word64 -> Int64) -> Parser Word64 -> Parser Int64
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Word64
PB.getFixed64)

instance HasCodec String where
  encode :: String -> ByteString
encode = String -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs
  decode :: ByteString -> Either Text String
decode = String -> Either Text String
forall a b. b -> Either a b
Right (String -> Either Text String)
-> (ByteString -> String) -> ByteString -> Either Text String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs

instance HasCodec Text where
  encode :: Text -> ByteString
encode = Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs
  decode :: ByteString -> Either Text Text
decode = Text -> Either Text Text
forall a b. b -> Either a b
Right (Text -> Either Text Text)
-> (ByteString -> Text) -> ByteString -> Either Text Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs

instance HasCodec BS.ByteString where
  encode :: ByteString -> ByteString
encode = ByteString -> ByteString
forall a. a -> a
id
  decode :: ByteString -> Either Text ByteString
decode = ByteString -> Either Text ByteString
forall a b. b -> Either a b
Right


defaultSDKAesonOptions :: String -> Options
defaultSDKAesonOptions :: String -> Options
defaultSDKAesonOptions prefix :: String
prefix = Int -> (String -> String) -> Options
aesonDrop (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
prefix) String -> String
snakeCase