packages feed

ribosome-host-0.9.9.9: lib/Ribosome/Host/RpcCall.hs

module Ribosome.Host.RpcCall where

import Data.MessagePack (Object (ObjectArray, ObjectNil))
import Exon (exon)

import Ribosome.Host.Class.Msgpack.Decode (pattern Msgpack, MsgpackDecode (fromMsgpack))
import Ribosome.Host.Class.Msgpack.Encode (toMsgpack)
import Ribosome.Host.Data.Request (Request (Request))
import Ribosome.Host.Data.RpcCall (RpcCall (RpcAtomic, RpcCallRequest, RpcFmap, RpcPure))

decodeAtom ::
  MsgpackDecode a =>
  [Object] ->
  Either Text ([Object], a)
decodeAtom = \case
  o : rest ->
    (rest,) <$> fromMsgpack o
  [] ->
    Left "Too few results in atomic call response"

foldAtomic :: RpcCall a -> ([Request], [Object] -> Either Text ([Object], a))
foldAtomic = \case
  RpcCallRequest req ->
    ([coerce req], decodeAtom)
  RpcPure a ->
    ([], Right . (,a))
  RpcFmap f a ->
    second (second (second f) .) (foldAtomic a)
  RpcAtomic f aa ab ->
    (reqsA <> reqsB, decode)
    where
      decode o = do
        (restA, a) <- decodeA o
        second (f a) <$> decodeB restA
      (reqsB, decodeB) =
        foldAtomic ab
      (reqsA, decodeA) =
        foldAtomic aa

checkLeftovers :: ([Object], a) -> Either Text a
checkLeftovers = \case
  ([], a) -> Right a
  (res, _) -> Left [exon|Excess results in atomic call response: #{show res}|]

atomicRequest :: [Request] -> Request
atomicRequest reqs =
  Request "nvim_call_atomic" [toMsgpack reqs]

atomicResult ::
  ([Object] -> Either Text ([Object], a)) ->
  Object ->
  Either Text a
atomicResult decode = \case
  ObjectArray [Msgpack res, ObjectNil] ->
    checkLeftovers =<< decode res
  ObjectArray [_, errs] ->
    Left (show errs)
  o ->
    Left ("Bad atomic result: " <> show o)

cata :: RpcCall a -> Either a (Request, Object -> Either Text a)
cata = \case
  RpcCallRequest req ->
    Right (req, fromMsgpack)
  RpcPure a ->
    Left a
  RpcFmap f a ->
    bimap f (second (second f .)) (cata a)
  a@RpcAtomic {} ->
    Right (bimap atomicRequest atomicResult (foldAtomic a))