Compare commits

..

5 Commits

Author SHA1 Message Date
Benjamin Eckel
ea1c153af4 fix: Fix the release action 2022-11-29 10:19:31 -06:00
Benjamin Eckel
9e30cd1932 remove generate file 2022-11-28 19:24:45 -06:00
Benjamin Eckel
3dccf9b5cf put back mistake 2022-11-28 19:22:05 -06:00
Benjamin Eckel
af090dbe4d Merge branch 'main' into v0.0.1 2022-11-28 19:21:13 -06:00
Benjamin Eckel
832a644e11 release: Bump to 0.0.1 2022-11-28 19:14:11 -06:00
7 changed files with 108 additions and 130 deletions

View File

@@ -21,5 +21,5 @@ jobs:
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_TOKEN }}
run: |
cd ruby
make publish RUBYGEMS_API_KEY=$RUBYGEMS_API_KEY
make publish

View File

@@ -18,6 +18,6 @@ handlePlugin plugin = do
exitSuccess) res
main = do
context <- Extism.newContext
context <- Extism.newContext ()
plugin <- Extism.pluginFromManifest context (manifest [wasmFile "../wasm/code.wasm"]) False
try handlePlugin plugin

View File

@@ -1,4 +1,4 @@
cabal-version: 3.0
cabal-version: 2.4
name: extism
version: 0.0.1
@@ -23,41 +23,22 @@ category: Plugins, WebAssembly
extra-source-files: CHANGELOG.md
library
exposed-modules: Extism
reexported-modules: Extism.Manifest
exposed-modules: Extism Extism.Manifest
-- Modules included in this library but not exported.
other-modules: Extism.Bindings
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends:
base >= 1.6.0
, bytestring
, json
, manifest
hs-source-dirs: src
default-language: Haskell2010
extra-libraries: extism
extra-lib-dirs: /usr/local/lib
library manifest
exposed-modules: Extism.Manifest
visibility: public
-- Modules included in this library but not exported.
other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends:
base
base ^>=4.16.1.0
, bytestring
, base64-bytestring
, json
hs-source-dirs: manifest
hs-source-dirs: src
default-language: Haskell2010
extra-libraries: extism
extra-lib-dirs: /usr/local/lib
Test-Suite extism-example
type: exitcode-stdio-1.0

View File

@@ -1,17 +1,36 @@
{-# LANGUAGE ForeignFunctionInterface #-}
module Extism (module Extism, module Extism.Manifest) where
import Data.Int
import Data.Word
import Control.Monad (void)
import GHC.Int
import GHC.Word
import Foreign.C.Types
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.C.String
import Foreign.Ptr
import Control.Monad (void)
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)
import Extism.Manifest (Manifest, toString, toJSONValue)
import Extism.Bindings
import Text.JSON (JSON, toJSObject, toJSString, encode, JSValue(JSNull, JSString))
import Extism.Manifest (Manifest, toString)
newtype ExtismContext = ExtismContext () deriving Show
foreign import ccall unsafe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext)
foreign import ccall unsafe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ())
foreign import ccall unsafe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> CBool -> IO Int32
foreign import ccall unsafe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> CBool -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
foreign import ccall unsafe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString
foreign import ccall unsafe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64
foreign import ccall unsafe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO ()
foreign import ccall unsafe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO ()
foreign import ccall unsafe "extism.h extism_version" extism_version :: IO CString
-- Context manages plugins
newtype Context = Context (ForeignPtr ExtismContext)
@@ -45,8 +64,8 @@ reset (Context ctx) =
withForeignPtr ctx extism_context_reset
-- Create a new context
newContext :: IO Context
newContext = do
newContext :: () -> IO Context
newContext () = do
ptr <- extism_context_new
fptr <- newForeignPtr extism_context_free ptr
return (Context fptr)
@@ -54,7 +73,7 @@ newContext = do
-- Execute a function with a new context that is destroyed when it returns
withContext :: (Context -> IO a) -> IO a
withContext f = do
ctx <- newContext
ctx <- newContext ()
f ctx
-- Create a plugin from a WASM module, `useWasi` determines if WASI should
@@ -107,13 +126,16 @@ updateManifest plugin manifest useWasi =
isValid :: Plugin -> Bool
isValid (Plugin _ p) = p >= 0
convertMaybeString Nothing = JSNull
convertMaybeString (Just s) = JSString (toJSString s)
-- Set configuration values for a plugin
setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool
setConfig (Plugin (Context ctx) plugin) x =
if plugin < 0
then return False
else
let obj = toJSObject [(k, toJSONValue v) | (k, v) <- x] in
let obj = toJSObject [(k, convertMaybeString v) | (k, v) <- x] in
let bs = toByteString (encode obj) in
let length = fromIntegral (B.length bs) in
unsafeUseAsCString bs (\s -> do

View File

@@ -1,26 +0,0 @@
{-# LANGUAGE ForeignFunctionInterface #-}
module Extism.Bindings where
import Foreign.C.Types
import Foreign.Ptr
import Foreign.C.String
import Data.Int
import Data.Word
newtype ExtismContext = ExtismContext () deriving Show
foreign import ccall unsafe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext)
foreign import ccall unsafe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ())
foreign import ccall unsafe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> CBool -> IO Int32
foreign import ccall unsafe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> CBool -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
foreign import ccall unsafe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString
foreign import ccall unsafe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64
foreign import ccall unsafe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO ()
foreign import ccall unsafe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO ()
foreign import ccall unsafe "extism.h extism_version" extism_version :: IO CString

View File

@@ -1,10 +1,7 @@
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module Extism.Manifest where
import Text.JSON
(
JSON,
JSValue(JSNull, JSString, JSArray),
toJSString, showJSON, makeObj, encode
)
@@ -12,12 +9,16 @@ import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as BS (unpack)
makeArray x = JSArray [toJSONValue a | a <- x]
valueOrNull f Nothing = JSNull
valueOrNull f (Just x) = f x
makeString s = JSString (toJSString s)
stringOrNull = valueOrNull makeString
makeArray f [] = JSNull
makeArray f x = JSArray [f a | a <- x]
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
mapObj f x = makeObj (filterNulls [(a, f b) | (a, b) <- x])
isNull JSNull = True
isNull _ = False
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
object x = makeObj $ filterNulls x
(.=) a b = (a, toJSONValue b)
newtype Memory = Memory
{
@@ -26,37 +27,32 @@ newtype Memory = Memory
class JSONValue a where
toJSONValue :: a -> JSValue
instance {-# OVERLAPS #-} (JSON a) => (JSONValue a) where
toJSONValue j = showJSON j
instance {-# OVERLAPS #-} (JSONValue a) => (JSONValue (Maybe a)) where
toJSONValue Nothing = JSNull
toJSONValue (Just x) = toJSONValue x
instance JSONValue Memory where
toJSONValue (Memory max) =
object [
"max" .= max
]
toJSONValue x =
case memoryMax x of
Nothing -> makeObj []
Just max -> makeObj [("max", showJSON max)]
data HTTPRequest = HTTPRequest
data HttpRequest = HttpRequest
{
url :: String
, header :: Maybe [(String, String)]
, header :: [(String, String)]
, method :: Maybe String
}
requestObj (HTTPRequest url header method) =
[
"url" .= url ,
"header" .= header,
"method" .= method
requestObj x =
let meth = stringOrNull $ method x in
let h = mapObj makeString $ header x in
filterNulls [
("url", makeString $ url x),
("header", h),
("method", meth)
]
instance JSONValue HTTPRequest where
instance JSONValue HttpRequest where
toJSONValue x =
object $ requestObj x
makeObj $ requestObj x
data WasmFile = WasmFile
{
@@ -66,11 +62,14 @@ data WasmFile = WasmFile
}
instance JSONValue WasmFile where
toJSONValue (WasmFile path name hash) =
object [
"path" .= path,
"name" .= name,
"hash" .= hash
toJSONValue x =
let path = makeString $ filePath x in
let name = stringOrNull $ fileName x in
let hash = stringOrNull $ fileHash x in
makeObj $ filterNulls [
("path", path),
("name", name),
("hash", hash)
]
data WasmCode = WasmCode
@@ -82,26 +81,30 @@ data WasmCode = WasmCode
instance JSONValue WasmCode where
toJSONValue (WasmCode x name hash) =
let bytes = BS.unpack $ B64.encode x in
object [
"data" .= bytes,
"name" .= name,
"hash" .= hash
toJSONValue x =
let bytes = makeString $ BS.unpack $ B64.encode $ codeBytes x in
let name = stringOrNull $ codeName x in
let hash = stringOrNull $ codeHash x in
makeObj $ filterNulls [
("data", bytes),
("name", name),
("hash", hash)
]
data WasmURL = WasmURL
{
req :: HTTPRequest
req :: HttpRequest
, urlName :: Maybe String
, urlHash :: Maybe String
}
instance JSONValue WasmURL where
toJSONValue (WasmURL req name hash) =
let request = requestObj $ req in
object $ "name" .= name : "hash" .= hash : request
toJSONValue x =
let request = requestObj $ req x in
let name = stringOrNull $ urlName x in
let hash = stringOrNull $ urlHash x in
makeObj $ filterNulls $ ("name", name) : ("hash", hash) : request
data Wasm = File WasmFile | Code WasmCode | URL WasmURL
@@ -118,7 +121,7 @@ wasmFile path =
wasmURL :: String -> String -> Wasm
wasmURL method url =
let r = HTTPRequest { url = url, header = Nothing, method = Just method } in
let r = HttpRequest { url = url, header = [], method = Just method } in
URL WasmURL { req = r, urlName = Nothing, urlHash = Nothing }
wasmCode :: B.ByteString -> Wasm
@@ -140,8 +143,8 @@ data Manifest = Manifest
{
wasm :: [Wasm]
, memory :: Maybe Memory
, config :: Maybe [(String, String)]
, allowedHosts :: Maybe [String]
, config :: [(String, String)]
, allowed_hosts :: [String]
}
manifest :: [Wasm] -> Manifest
@@ -149,29 +152,32 @@ manifest wasm =
Manifest {
wasm = wasm,
memory = Nothing,
config = Nothing,
allowedHosts = Nothing
config = [],
allowed_hosts = []
}
withConfig :: Manifest -> [(String, String)] -> Manifest
withConfig m config =
m { config = Just config }
m { config = config }
withHosts :: Manifest -> [String] -> Manifest
withHosts m hosts =
m { allowedHosts = Just hosts }
m { allowed_hosts = hosts }
instance JSONValue Manifest where
toJSONValue (Manifest wasm memory config hosts) =
let w = makeArray wasm in
object [
"wasm" .= w,
"memory" .= memory,
"config" .= config,
"allowed_hosts" .= hosts
toJSONValue x =
let w = makeArray toJSONValue $ wasm x in
let mem = valueOrNull toJSONValue $ memory x in
let c = mapObj makeString $ config x in
let hosts = makeArray makeString $ allowed_hosts x in
makeObj $ filterNulls [
("wasm", w),
("memory", mem),
("config", c),
("allowed_hosts", hosts)
]
toString :: (JSONValue a) => a -> String
toString v =
encode (toJSONValue v)
toString :: Manifest -> String
toString manifest =
encode (toJSONValue manifest)

View File

@@ -1,4 +1,3 @@
RUBYGEMS_API_KEY ?=
.PHONY: prepare test
@@ -10,15 +9,11 @@ test: prepare
bundle exec rake test
clean:
rm -f extism-*.gem
publish-local: clean prepare
gem build extism.gemspec
gem push extism-*.gem
rm extism-*.gem
publish: clean prepare
gem build extism.gemspec
GEM_HOST_API_KEY=$(RUBYGEMS_API_KEY) gem push extism-*.gem
gem push extism-*.gem
lint:
bundle exec rufo --check .