module Extism (module Extism, module Extism.Manifest) where
import Data.Int
import Data.Word
import Control.Monad (void)
import Foreign.ForeignPtr
import Foreign.C.String
import Foreign.Ptr
import Data.ByteString as B
import Data.ByteString.Internal (c2w, w2c)
import Data.ByteString.Unsafe (unsafeUseAsCString)
import Data.Bifunctor (second)
import Text.JSON (encode, toJSObject, showJSON)
import Extism.Manifest (Manifest, toString)
import Extism.Bindings
newtype Context = Context (ForeignPtr ExtismContext)
data Plugin = Plugin Context Int32
data LogLevel = Error | Warn | Info | Debug | Trace deriving (Int -> LogLevel -> ShowS
[LogLevel] -> ShowS
LogLevel -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LogLevel] -> ShowS
$cshowList :: [LogLevel] -> ShowS
show :: LogLevel -> String
$cshow :: LogLevel -> String
showsPrec :: Int -> LogLevel -> ShowS
$cshowsPrec :: Int -> LogLevel -> ShowS
Show)
newtype Error = ExtismError String deriving Int -> Error -> ShowS
[Error] -> ShowS
Error -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Error] -> ShowS
$cshowList :: [Error] -> ShowS
show :: Error -> String
$cshow :: Error -> String
showsPrec :: Int -> Error -> ShowS
$cshowsPrec :: Int -> Error -> ShowS
Show
type Result a = Either Error a
toByteString :: String -> ByteString
toByteString :: String -> ByteString
toByteString String
x = [Word8] -> ByteString
B.pack (forall a b. (a -> b) -> [a] -> [b]
Prelude.map Char -> Word8
c2w String
x)
fromByteString :: ByteString -> String
fromByteString :: ByteString -> String
fromByteString ByteString
bs = forall a b. (a -> b) -> [a] -> [b]
Prelude.map Word8 -> Char
w2c forall a b. (a -> b) -> a -> b
$ ByteString -> [Word8]
B.unpack ByteString
bs
extismVersion :: () -> IO String
extismVersion :: () -> IO String
extismVersion () = do
CString
v <- IO CString
extism_version
CString -> IO String
peekCString CString
v
reset :: Context -> IO ()
reset :: Context -> IO ()
reset (Context ForeignPtr ExtismContext
ctx) =
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx Ptr ExtismContext -> IO ()
extism_context_reset
newContext :: IO Context
newContext :: IO Context
newContext = do
Ptr ExtismContext
ptr <- IO (Ptr ExtismContext)
extism_context_new
ForeignPtr ExtismContext
fptr <- forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FunPtr (Ptr ExtismContext -> IO ())
extism_context_free Ptr ExtismContext
ptr
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr ExtismContext -> Context
Context ForeignPtr ExtismContext
fptr)
withContext :: (Context -> IO a) -> IO a
withContext :: forall a. (Context -> IO a) -> IO a
withContext Context -> IO a
f = do
Context
ctx <- IO Context
newContext
Context -> IO a
f Context
ctx
plugin :: Context -> B.ByteString -> Bool -> IO (Result Plugin)
plugin :: Context -> ByteString -> Bool -> IO (Result Plugin)
plugin Context
c ByteString
wasm Bool
useWasi =
let length :: Word64
length = forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
B.length ByteString
wasm) in
let wasi :: CBool
wasi = forall a. Num a => Integer -> a
fromInteger (if Bool
useWasi then Integer
1 else Integer
0) in
let Context ForeignPtr ExtismContext
ctx = Context
c in
do
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (\Ptr ExtismContext
ctx -> do
Int32
p <- forall a. ByteString -> (CString -> IO a) -> IO a
unsafeUseAsCString ByteString
wasm (\CString
s ->
Ptr ExtismContext
-> Ptr Word8
-> Word64
-> Ptr (Ptr ExtismFunction)
-> Word64
-> CBool
-> IO Int32
extism_plugin_new Ptr ExtismContext
ctx (forall a b. Ptr a -> Ptr b
castPtr CString
s) Word64
length forall a. Ptr a
nullPtr Word64
0 CBool
wasi )
if Int32
p forall a. Ord a => a -> a -> Bool
< Int32
0 then do
CString
err <- Ptr ExtismContext -> Int32 -> IO CString
extism_error Ptr ExtismContext
ctx (-Int32
1)
String
e <- CString -> IO String
peekCString CString
err
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left (String -> Error
ExtismError String
e)
else
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right (Context -> Int32 -> Plugin
Plugin Context
c Int32
p))
pluginFromManifest :: Context -> Manifest -> Bool -> IO (Result Plugin)
pluginFromManifest :: Context -> Manifest -> Bool -> IO (Result Plugin)
pluginFromManifest Context
ctx Manifest
manifest Bool
useWasi =
let wasm :: ByteString
wasm = String -> ByteString
toByteString forall a b. (a -> b) -> a -> b
$ forall a. JSON a => a -> String
toString Manifest
manifest in
Context -> ByteString -> Bool -> IO (Result Plugin)
plugin Context
ctx ByteString
wasm Bool
useWasi
update :: Plugin -> B.ByteString -> Bool -> IO (Result ())
update :: Plugin -> ByteString -> Bool -> IO (Result ())
update (Plugin (Context ForeignPtr ExtismContext
ctx) Int32
id) ByteString
wasm Bool
useWasi =
let length :: Word64
length = forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
B.length ByteString
wasm) in
let wasi :: CBool
wasi = forall a. Num a => Integer -> a
fromInteger (if Bool
useWasi then Integer
1 else Integer
0) in
do
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (\Ptr ExtismContext
ctx -> do
CBool
b <- forall a. ByteString -> (CString -> IO a) -> IO a
unsafeUseAsCString ByteString
wasm (\CString
s ->
Ptr ExtismContext
-> Int32
-> Ptr Word8
-> Word64
-> Ptr (Ptr ExtismFunction)
-> Word64
-> CBool
-> IO CBool
extism_plugin_update Ptr ExtismContext
ctx Int32
id (forall a b. Ptr a -> Ptr b
castPtr CString
s) Word64
length forall a. Ptr a
nullPtr Word64
0 CBool
wasi)
if CBool
b forall a. Ord a => a -> a -> Bool
<= CBool
0 then do
CString
err <- Ptr ExtismContext -> Int32 -> IO CString
extism_error Ptr ExtismContext
ctx (-Int32
1)
String
e <- CString -> IO String
peekCString CString
err
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left (String -> Error
ExtismError String
e)
else
forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. b -> Either a b
Right ()))
updateManifest :: Plugin -> Manifest -> Bool -> IO (Result ())
updateManifest :: Plugin -> Manifest -> Bool -> IO (Result ())
updateManifest Plugin
plugin Manifest
manifest Bool
useWasi =
let wasm :: ByteString
wasm = String -> ByteString
toByteString forall a b. (a -> b) -> a -> b
$ forall a. JSON a => a -> String
toString Manifest
manifest in
Plugin -> ByteString -> Bool -> IO (Result ())
update Plugin
plugin ByteString
wasm Bool
useWasi
isValid :: Plugin -> Bool
isValid :: Plugin -> Bool
isValid (Plugin Context
_ Int32
p) = Int32
p forall a. Ord a => a -> a -> Bool
>= Int32
0
setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool
setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool
setConfig (Plugin (Context ForeignPtr ExtismContext
ctx) Int32
plugin) [(String, Maybe String)]
x =
if Int32
plugin forall a. Ord a => a -> a -> Bool
< Int32
0
then forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
else
let obj :: JSObject JSValue
obj = forall a. [(String, a)] -> JSObject a
toJSObject [(String
k, forall a. JSON a => a -> JSValue
showJSON Maybe String
v) | (String
k, Maybe String
v) <- [(String, Maybe String)]
x] in
let bs :: ByteString
bs = String -> ByteString
toByteString (forall a. JSON a => a -> String
encode JSObject JSValue
obj) in
let length :: Int64
length = forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
B.length ByteString
bs) in
forall a. ByteString -> (CString -> IO a) -> IO a
unsafeUseAsCString ByteString
bs (\CString
s -> do
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (\Ptr ExtismContext
ctx -> do
CBool
b <- Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
extism_plugin_config Ptr ExtismContext
ctx Int32
plugin (forall a b. Ptr a -> Ptr b
castPtr CString
s) Int64
length
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ CBool
b forall a. Eq a => a -> a -> Bool
/= CBool
0))
levelStr :: LogLevel -> String
levelStr LogLevel
Error = String
"error"
levelStr LogLevel
Debug = String
"debug"
levelStr LogLevel
Warn = String
"warn"
levelStr LogLevel
Trace = String
"trace"
levelStr LogLevel
Info = String
"info"
setLogFile :: String -> LogLevel -> IO Bool
setLogFile :: String -> LogLevel -> IO Bool
setLogFile String
filename LogLevel
level =
let s :: String
s = LogLevel -> String
levelStr LogLevel
level in
forall a. String -> (CString -> IO a) -> IO a
withCString String
filename (\CString
f ->
forall a. String -> (CString -> IO a) -> IO a
withCString String
s (\CString
l -> do
CBool
b <- CString -> CString -> IO CBool
extism_log_file CString
f CString
l
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ CBool
b forall a. Eq a => a -> a -> Bool
/= CBool
0))
functionExists :: Plugin -> String -> IO Bool
functionExists :: Plugin -> String -> IO Bool
functionExists (Plugin (Context ForeignPtr ExtismContext
ctx) Int32
plugin) String
name = do
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (\Ptr ExtismContext
ctx -> do
CBool
b <- forall a. String -> (CString -> IO a) -> IO a
withCString String
name (Ptr ExtismContext -> Int32 -> CString -> IO CBool
extism_plugin_function_exists Ptr ExtismContext
ctx Int32
plugin)
if CBool
b forall a. Eq a => a -> a -> Bool
== CBool
1 then forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True else forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False)
call :: Plugin -> String -> B.ByteString -> IO (Result B.ByteString)
call :: Plugin -> String -> ByteString -> IO (Result ByteString)
call (Plugin (Context ForeignPtr ExtismContext
ctx) Int32
plugin) String
name ByteString
input =
let length :: Word64
length = forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
B.length ByteString
input) in
do
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (\Ptr ExtismContext
ctx -> do
Int32
rc <- forall a. String -> (CString -> IO a) -> IO a
withCString String
name (\CString
name ->
forall a. ByteString -> (CString -> IO a) -> IO a
unsafeUseAsCString ByteString
input (\CString
input ->
Ptr ExtismContext
-> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
extism_plugin_call Ptr ExtismContext
ctx Int32
plugin CString
name (forall a b. Ptr a -> Ptr b
castPtr CString
input) Word64
length))
CString
err <- Ptr ExtismContext -> Int32 -> IO CString
extism_error Ptr ExtismContext
ctx Int32
plugin
if CString
err forall a. Eq a => a -> a -> Bool
/= forall a. Ptr a
nullPtr
then do String
e <- CString -> IO String
peekCString CString
err
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left (String -> Error
ExtismError String
e)
else if Int32
rc forall a. Eq a => a -> a -> Bool
== Int32
0
then do
Word64
length <- Ptr ExtismContext -> Int32 -> IO Word64
extism_plugin_output_length Ptr ExtismContext
ctx Int32
plugin
Ptr Word8
ptr <- Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
extism_plugin_output_data Ptr ExtismContext
ctx Int32
plugin
ByteString
buf <- CStringLen -> IO ByteString
packCStringLen (forall a b. Ptr a -> Ptr b
castPtr Ptr Word8
ptr, forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
length)
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right ByteString
buf
else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left (String -> Error
ExtismError String
"Call failed"))
free :: Plugin -> IO ()
free :: Plugin -> IO ()
free (Plugin (Context ForeignPtr ExtismContext
ctx) Int32
plugin) =
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ExtismContext
ctx (Ptr ExtismContext -> Int32 -> IO ()
`extism_plugin_free` Int32
plugin)