extism-pdk-0.1.0.0: src/Extism/PDK.hs
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
module Extism.PDK (module Extism.PDK, module Extism.Manifest) where
import Extism.PDK.Bindings
import Extism.JSON(JSValue, JSON)
import Extism.Manifest(toString)
import Data.Word
import Data.Int
import Data.ByteString as B
import Data.ByteString.Internal (c2w, w2c)
import Data.ByteString.Unsafe (unsafeUseAsCString)
import Text.JSON(JSON, decode, encode, resultToEither)
import qualified Extism.PDK.MsgPack(MsgPack, decode, encode)
newtype JSONValue a = JSONValue a
newtype MsgPackValue a = MsgPackValue a
-- | Represents a block of memory
data Memory = Memory MemoryOffset MemoryLength
-- | Helper function to convert a string to a bytestring
toByteString :: String -> ByteString
toByteString x = B.pack (Prelude.map c2w x)
-- | Helper function to convert a bytestring to a string
fromByteString :: ByteString -> String
fromByteString bs = Prelude.map w2c $ B.unpack bs
class FromBytes a where
fromBytes :: ByteString -> a
class ToBytes a where
toBytes :: a -> ByteString
instance FromBytes ByteString where
fromBytes bs = bs
instance ToBytes ByteString where
toBytes bs = bs
instance FromBytes String where
fromBytes = fromByteString
instance ToBytes String where
toBytes = toByteString
instance JSON a => FromBytes (JSONValue a) where
fromBytes x =
case resultToEither $ decode (fromByteString x) of
Left e -> error e
Right y -> JSONValue y
instance JSON a => ToBytes (JSONValue a) where
toBytes (JSONValue x) = toByteString (encode x)
instance Extism.PDK.MsgPack.MsgPack a => FromBytes (MsgPackValue a) where
fromBytes x =
case Extism.PDK.MsgPack.decode x of
Left e -> error e
Right y -> MsgPackValue y
instance Extism.PDK.MsgPack.MsgPack a => ToBytes (MsgPackValue a) where
toBytes (MsgPackValue x) = Extism.PDK.MsgPack.encode x
-- | Get plugin input as 'ByteString'
input :: FromBytes a => IO a
input = do
len <- extismInputLength
fromBytes <$> readInputBytes len
-- | Get plugin input as 'Memory' block
inputMemory :: IO Memory
inputMemory = do
len <- extismInputLength
offs <- extismAlloc len
Prelude.mapM_ (\x ->
extismStoreU8 (offs + x) <$> extismInputLoadU8 x) [0, 1 .. len]
return $ Memory offs len
-- | Get input as 'JSON'
inputJSON :: JSON a => IO (Maybe a)
inputJSON = do
s <- input :: IO String
case resultToEither $ decode s of
Left _ -> return Nothing
Right x -> return (Just x)
-- | Load data from 'Memory' block
load :: FromBytes a => Memory -> IO a
load (Memory offs len) =
fromBytes <$> readBytes offs len
-- | Store data into a 'Memory' block
store :: ToBytes a => Memory -> a -> IO ()
store (Memory offs len) a =
let bs = toBytes a in
writeBytes offs len bs
-- | Set plugin output to the provided 'Memory' block
outputMemory :: Memory -> IO ()
outputMemory (Memory offs len) =
extismSetOutput offs len
-- | Set plugin output to the provided 'ByteString'
output :: ToBytes a => a -> IO ()
output x =
let bs = toBytes x in
let len = fromIntegral $ B.length bs in
do
offs <- extismAlloc len
b <- store (Memory offs len) bs
extismSetOutput offs len
-- | Set plugin output to a JSON encoded version of the provided value
outputJSON :: JSON a => a -> IO ()
outputJSON x =
output (toString x)
-- | Load string from 'Memory' block
loadString :: Memory -> IO String
loadString mem = do
bs <- load mem
return $ fromByteString bs
-- | Store string in 'Memory' block
storeString :: Memory -> String -> IO ()
storeString mem s =
let bs = toByteString s in
store mem bs
-- | Allocate a new 'Memory' block
alloc :: Int -> IO Memory
alloc n =
let len = fromIntegral n in
do
offs <- extismAlloc len
return $ Memory offs len
-- | Free a 'Memory' block
free :: Memory -> IO ()
free (Memory 0 _) = return ()
free (Memory _ 0) = return ()
free (Memory offs _) =
extismFree offs
-- | Allocate a new 'Memory' block and copy the contents of the provided 'ByteString'
allocByteString :: ByteString -> IO Memory
allocByteString bs = do
mem <- alloc (B.length bs)
store mem bs
return mem
-- | Allocate a new 'Memory' block and copy the contents of the provided 'String'
allocString :: String -> IO Memory
allocString s =
let bs = toByteString s in
allocByteString bs
-- | Get the offset of a 'Memory' block
memoryOffset (Memory offs _) = offs
-- | Get the length of a 'Memory' block
memoryLength (Memory _ len) = len
-- | Find 'Memory' block by offset
findMemory offs = do
len <- extismLength offs
return $ Memory offs len
-- | Get a variable from the Extism runtime
getVar :: String -> IO (Maybe ByteString)
getVar key = do
k <- allocString key
v <- extismGetVar (memoryOffset k)
free k
if v == 0 then
return Nothing
else do
mem <- findMemory v
bs <- load mem
free mem
return (Just bs)
-- | Set a variable
setVar :: ToBytes a => String -> Maybe a -> IO ()
setVar key Nothing = do
k <- allocString key
extismSetVar (memoryOffset k) 0
free k
setVar key (Just v) = do
k <- allocString key
x <- allocByteString (toBytes v)
extismSetVar (memoryOffset k) (memoryOffset x)
free k
free x
-- | Get a configuration value
getConfig :: String -> IO (Maybe String)
getConfig key = do
k <- allocString key
v <- extismGetConfig (memoryOffset k)
free k
if v == 0 then
return Nothing
else do
mem <- findMemory v
s <- loadString mem
free mem
return $ Just s
-- | Set the current error message
setError :: String -> IO ()
setError msg = do
s <- allocString msg
extismSetError $ memoryOffset s
free s
data LogLevel = Info | Debug | Warn | Error
log :: LogLevel -> String -> IO ()
log Info msg = do
s <- allocString msg
extismLogInfo (memoryOffset s)
free s
log Debug msg = do
s <- allocString msg
extismLogDebug (memoryOffset s)
free s
log Warn msg = do
s <- allocString msg
extismLogWarn (memoryOffset s)
free s
log Error msg = do
s <- allocString msg
extismLogError (memoryOffset s)
free s