packages feed

ribosome-0.3.0.0: lib/Ribosome/Nvim/Api/GenerateData.hs

{-# LANGUAGE TemplateHaskell #-}

module Ribosome.Nvim.Api.GenerateData where

import Data.ByteString (ByteString)
import Data.Int (Int64)
import Data.MessagePack (Object(ObjectExt))
import Language.Haskell.TH
import Neovim.Plugin.Classes (FunctionName(F))

import Ribosome.Msgpack.Decode (MsgpackDecode)
import Ribosome.Msgpack.Encode (MsgpackEncode)
import Ribosome.Msgpack.Util (illegalType)
import Ribosome.Nvim.Api.Generate (FunctionData(FunctionData), generateFromApi)
import Ribosome.Nvim.Api.RpcCall (AsyncRpcCall(..), RpcCall(..), SyncRpcCall(..))

dataSig :: [Type] -> Name -> Bool -> DecQ
dataSig types name async = do
  returnType <- if async then [t|AsyncRpcCall|] else [t|SyncRpcCall|]
  sigD name . return . foldr (AppT . AppT ArrowT) returnType $ types

dataBody :: String -> Name -> Bool -> [Name] -> DecQ
dataBody apiName name async params =
  funD name [clause (varP <$> params) (normalB $ appE syncCtor rpcCall) []]
  where
    rpcCall = [|RpcCall|] `appE` funcName `appE` listE (toObjVar <$> params)
    funcName = [|F . fromString|] `appE` (litE . stringL $ apiName)
    toObjVar v = [|toMsgpack $(varE v)|]
    syncCtor = if async then [|AsyncRpcCall|] else [|SyncRpcCall|]

genCallData :: FunctionData -> DecsQ
genCallData (FunctionData apiName name async names types _) = do
  sig <- dataSig types name async
  body <- dataBody apiName name async names
  return [sig, body]

extData :: Name -> DecQ
extData name =
  dataD (return []) name [] Nothing [ctor] (deriv ["Eq", "Show"])
  where
    ctor = normalC name [(Bang NoSourceUnpackedness SourceStrict,) <$> [t|ByteString|]]
    deriv = return . return . DerivClause Nothing . (ConT . mkName <$>)

decClause :: Name -> Int64 -> ClauseQ
decClause name number = do
  bytesVar <- newName "bytes"
  clause [pattern bytesVar] (decBody bytesVar) []
  where
    pattern bytesVar = conP (mkName "ObjectExt") [(litP . integerL . fromIntegral) number, varP bytesVar]
    decBody bytesVar = (normalB [|return $ $(conE name) $(varE bytesVar)|])

decErrorClause :: Name -> ClauseQ
decErrorClause name = do
  objectVar <- newName "object"
  clause [varP objectVar] (decBody objectVar) []
  where
    nameString = nameBase name
    decBody objectVar = normalB [|illegalType nameString $(varE objectVar)|]

encClause :: Name -> Int64 -> ClauseQ
encClause name number = do
  bytesVar <- newName "bytes"
  clause [conP name [varP bytesVar]] (encBody bytesVar) []
  where
    encBody bytesVar = normalB [|ObjectExt $((litE . integerL . fromIntegral) number) $(varE bytesVar)|]

extDataCodec :: Name -> Int64 -> DecsQ
extDataCodec name number = do
  dec <- inst [t|MsgpackDecode|] [method "fromMsgpack" [decClause name number, decErrorClause name]]
  enc <- inst [t|MsgpackEncode|] [method "toMsgpack" [encClause name number]]
  return [dec, enc]
  where
    inst t = instanceD (return []) (tpe t)
    tpe = (`appT` conT name)
    method methodName clauses = funD (mkName methodName) clauses

genExtTypes :: Name -> Int64 -> DecsQ
genExtTypes name number = do
  dat <- extData name
  codec <- extDataCodec name number
  return (dat : codec)

generateData :: DecsQ
generateData =
  generateFromApi genCallData genExtTypes