module Extism.JSON ( module Extism.JSON, module Text.JSON ) where import Text.JSON import qualified Data.ByteString as B import Data.ByteString.Internal (c2w, w2c) import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Char8 as BS (unpack) data Nullable a = Null | NotNull a makeArray :: [a] -> JSValue makeArray [a] x = [JSValue] -> JSValue JSArray [forall a. JSON a => a -> JSValue showJSON a a | a a <- [a] x] isNull :: JSValue -> Bool isNull JSValue JSNull = Bool True isNull JSValue _ = Bool False filterNulls :: [(a, JSValue)] -> [(a, JSValue)] filterNulls [(a, JSValue)] obj = [(a a, JSValue b) | (a a, JSValue b) <- [(a, JSValue)] obj, Bool -> Bool not (JSValue -> Bool isNull JSValue b)] object :: [(String, JSValue)] -> JSValue object [(String, JSValue)] x = [(String, JSValue)] -> JSValue makeObj forall a b. (a -> b) -> a -> b $ forall {a}. [(a, JSValue)] -> [(a, JSValue)] filterNulls [(String, JSValue)] x objectWithNulls :: [(String, JSValue)] -> JSValue objectWithNulls [(String, JSValue)] x = [(String, JSValue)] -> JSValue makeObj [(String, JSValue)] x nonNull :: a -> Nullable a nonNull a x = forall a. a -> Nullable a NotNull a x null' :: Nullable a null' = forall a. Nullable a Null .= :: a -> a -> (a, JSValue) (.=) a a a b = (a a, forall a. JSON a => a -> JSValue showJSON a b) toNullable :: Maybe a -> Nullable a toNullable (Just a x) = forall a. a -> Nullable a NotNull a x toNullable Maybe a Nothing = forall a. Nullable a Null fromNullable :: Nullable a -> Maybe a fromNullable (NotNull a x) = forall a. a -> Maybe a Just a x fromNullable Nullable a Null = forall a. Maybe a Nothing fromNotNull :: Nullable a -> a fromNotNull (NotNull a x) = a x fromNotNull Nullable a Null = forall a. HasCallStack => String -> a error String "Value is Null" mapNullable :: (t -> a) -> Nullable t -> Nullable a mapNullable t -> a f Nullable t Null = forall a. Nullable a Null mapNullable t -> a f (NotNull t x) = forall a. a -> Nullable a NotNull (t -> a f t x) .? :: JSValue -> String -> Nullable a (.?) (JSObject JSObject JSValue a) String k = case forall a. JSON a => String -> JSObject JSValue -> Result a valFromObj String k JSObject JSValue a of Ok a x -> forall a. a -> Nullable a NotNull a x Error String _ -> forall a. Nullable a Null (.?) JSValue _ String _ = forall a. Nullable a Null .?? :: [(a, a)] -> a -> Nullable a (.??) [(a, a)] a a k = forall {a}. Maybe a -> Nullable a toNullable forall a b. (a -> b) -> a -> b $ forall a b. Eq a => a -> [(a, b)] -> Maybe b lookup a k [(a, a)] a find :: JSON a => String -> JSValue -> Nullable a find :: forall a. JSON a => String -> JSValue -> Nullable a find String k JSValue obj = JSValue obj forall {a}. JSON a => JSValue -> String -> Nullable a .? String k update :: JSON a => String -> a -> JSValue -> JSValue update :: forall a. JSON a => String -> a -> JSValue -> JSValue update String k a v (JSObject JSObject JSValue obj) = [(String, JSValue)] -> JSValue object forall a b. (a -> b) -> a -> b $ (forall e. JSObject e -> [(String, e)] fromJSObject JSObject JSValue obj) forall a. [a] -> [a] -> [a] ++ [String k forall {a} {a}. JSON a => a -> a -> (a, JSValue) .= a v] instance JSON a => JSON (Nullable a) where showJSON :: Nullable a -> JSValue showJSON (NotNull a x) = forall a. JSON a => a -> JSValue showJSON a x showJSON Nullable a Null = JSValue JSNull readJSON :: JSValue -> Result (Nullable a) readJSON JSValue JSNull = forall a. a -> Result a Ok forall a. Nullable a Null readJSON JSValue x = forall a. JSON a => JSValue -> Result a readJSON JSValue x newtype Base64 = Base64 B.ByteString instance JSON Base64 where showJSON :: Base64 -> JSValue showJSON (Base64 ByteString bs) = forall a. JSON a => a -> JSValue showJSON (ByteString -> String BS.unpack forall a b. (a -> b) -> a -> b $ ByteString -> ByteString B64.encode ByteString bs) readJSON :: JSValue -> Result Base64 readJSON (JSString JSString s) = let toByteString :: String -> ByteString toByteString String x = [Word8] -> ByteString B.pack (forall a b. (a -> b) -> [a] -> [b] Prelude.map Char -> Word8 c2w String x) in case ByteString -> Either String ByteString B64.decode (String -> ByteString toByteString (JSString -> String fromJSString JSString s)) of Left String msg -> forall a. String -> Result a Error String msg Right ByteString d -> forall a. a -> Result a Ok (ByteString -> Base64 Base64 ByteString d)