extism-1.0.0.0: src/Extism/Encoding.hs
{-# LANGUAGE FlexibleInstances #-}
-- |
-- Extism.Encoding handles how values are encoded to be copied in and out of Wasm linear memory
module Extism.Encoding
( fromByteString,
toByteString,
Error (..),
Result (..),
ToBytes (..),
FromBytes (..),
Encoding (..),
JSON (..),
)
where
import Data.Binary.Get (getDoublele, getFloatle, getInt32le, getInt64le, getWord32le, getWord64le, runGetOrFail)
import Data.Binary.Put (putDoublele, putFloatle, putInt32le, putInt64le, putWord32le, putWord64le, runPut)
import qualified Data.ByteString as B
import Data.ByteString.Internal (c2w, unsafePackLenAddress, w2c)
import Data.Int
import Data.Word
import qualified Text.JSON (JSValue, Result (..), decode, encode, showJSON, toJSObject)
import qualified Text.JSON.Generic (Data, decodeJSON, encodeJSON, fromJSON, toJSON)
-- | Helper function to convert a 'String' to a 'ByteString'
toByteString :: String -> B.ByteString
toByteString x = B.pack (map c2w x)
-- | Helper function to convert a 'ByteString' to a 'String'
fromByteString :: B.ByteString -> String
fromByteString bs = map w2c $ B.unpack bs
-- | Extism error
newtype Error = ExtismError String deriving (Show, Eq)
-- | Result type
type Result a = Either Error a
-- Used to convert a value into linear memory
class ToBytes a where
toBytes :: a -> B.ByteString
-- Used to read a value from linear memory
class FromBytes a where
fromBytes :: B.ByteString -> Result a
-- Encoding is used to indicate a type implements both `ToBytes` and `FromBytes`
class (ToBytes a, FromBytes a) => Encoding a
instance ToBytes () where
toBytes () = toByteString ""
instance FromBytes () where
fromBytes _ = Right ()
instance ToBytes B.ByteString where
toBytes x = x
instance FromBytes B.ByteString where
fromBytes = Right
instance ToBytes [Char] where
toBytes = toByteString
instance FromBytes [Char] where
fromBytes bs =
Right $ fromByteString bs
instance ToBytes Int32 where
toBytes i = B.toStrict (runPut (putInt32le i))
instance FromBytes Int32 where
fromBytes bs =
case runGetOrFail getInt32le (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
instance ToBytes Int64 where
toBytes i = B.toStrict (runPut (putInt64le i))
instance FromBytes Int64 where
fromBytes bs =
case runGetOrFail getInt64le (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
instance ToBytes Word32 where
toBytes i = B.toStrict (runPut (putWord32le i))
instance FromBytes Word32 where
fromBytes bs =
case runGetOrFail getWord32le (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
instance ToBytes Word64 where
toBytes i = B.toStrict (runPut (putWord64le i))
instance FromBytes Word64 where
fromBytes bs =
case runGetOrFail getWord64le (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
instance ToBytes Float where
toBytes i = B.toStrict (runPut (putFloatle i))
instance FromBytes Float where
fromBytes bs =
case runGetOrFail getFloatle (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
instance ToBytes Double where
toBytes i = B.toStrict (runPut (putDoublele i))
instance FromBytes Double where
fromBytes bs =
case runGetOrFail getDoublele (B.fromStrict bs) of
Left (_, _, e) -> Left (ExtismError e)
Right (_, _, x) -> Right x
-- Wraps a `JSON` value for input/output
newtype JSON x = JSON x
instance (Text.JSON.Generic.Data a) => ToBytes (JSON a) where
toBytes (JSON x) =
toByteString $ Text.JSON.Generic.encodeJSON x
instance (Text.JSON.Generic.Data a) => FromBytes (JSON a) where
fromBytes bs =
let x = Text.JSON.decode (fromByteString bs)
in case x of
Text.JSON.Error e -> Left (ExtismError e)
Text.JSON.Ok x ->
case Text.JSON.Generic.fromJSON (x :: Text.JSON.JSValue) of
Text.JSON.Error e -> Left (ExtismError e)
Text.JSON.Ok x -> Right (JSON x)