module Ribosome.Host.Interpreter.Process.Socket where

import Data.Serialize (Serialize)
import qualified Network.Socket as Socket
import Network.Socket (socketToHandle)
import Path (toFilePath)
import Polysemy.Process (Process, ProcessOptions, interpretProcessHandles)
import Polysemy.Process.Data.ProcessError (ProcessError)
import System.IO (Handle, IOMode (ReadWriteMode))

import Ribosome.Host.Data.BootError (BootError (BootError))
import Ribosome.Host.Data.NvimSocket (NvimSocket (NvimSocket))
import Ribosome.Host.Interpreter.Process.Cereal (interpretProcessInputCereal, interpretProcessOutputCereal)

withSocket ::
  Members [Reader NvimSocket, Resource, Error BootError, Embed IO] r =>
  (Handle -> Sem r a) ->
  Sem r a
withSocket :: forall (r :: EffectRow) a.
Members
  '[Reader NvimSocket, Resource, Error BootError, Embed IO] r =>
(Handle -> Sem r a) -> Sem r a
withSocket Handle -> Sem r a
use =
  Sem r Socket
-> (Socket -> Sem r ()) -> (Socket -> Sem r a) -> Sem r a
forall (r :: EffectRow) a c b.
Member Resource r =>
Sem r a -> (a -> Sem r c) -> (a -> Sem r b) -> Sem r b
bracket Sem r Socket
acquire Socket -> Sem r ()
release \ Socket
socket ->
    Handle -> Sem r a
use (Handle -> Sem r a) -> Sem r Handle -> Sem r a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO Handle -> Sem r Handle
forall (m :: * -> *) (r :: EffectRow) a.
Member (Embed m) r =>
m a -> Sem r a
embed (Socket -> IOMode -> IO Handle
socketToHandle Socket
socket IOMode
ReadWriteMode)
  where
    acquire :: Sem r Socket
acquire = do
      NvimSocket Path Abs File
path <- Sem r NvimSocket
forall i (r :: EffectRow). Member (Reader i) r => Sem r i
ask
      Either BootError Socket -> Sem r Socket
forall e (r :: EffectRow) a.
Member (Error e) r =>
Either e a -> Sem r a
fromEither (Either BootError Socket -> Sem r Socket)
-> (Either Text Socket -> Either BootError Socket)
-> Either Text Socket
-> Sem r Socket
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> BootError)
-> Either Text Socket -> Either BootError Socket
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Text -> BootError
BootError (Either Text Socket -> Sem r Socket)
-> Sem r (Either Text Socket) -> Sem r Socket
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO Socket -> Sem r (Either Text Socket)
forall (r :: EffectRow) a.
Member (Embed IO) r =>
IO a -> Sem r (Either Text a)
tryAny do
        Socket
socket <- Family -> SocketType -> ProtocolNumber -> IO Socket
Socket.socket Family
Socket.AF_UNIX SocketType
Socket.Stream ProtocolNumber
0
        Socket
socket Socket -> IO () -> IO Socket
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Socket -> SockAddr -> IO ()
Socket.connect Socket
socket (String -> SockAddr
Socket.SockAddrUnix (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
path))
    release :: Socket -> Sem r ()
release =
      IO () -> Sem r ()
forall (r :: EffectRow). Member (Embed IO) r => IO () -> Sem r ()
tryAny_ (IO () -> Sem r ()) -> (Socket -> IO ()) -> Socket -> Sem r ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Socket -> IO ()
Socket.close

interpretProcessCerealSocket ::
   a r .
  Serialize a =>
  Members [Reader NvimSocket, Error BootError, Log, Resource, Race, Async, Embed IO] r =>
  ProcessOptions ->
  InterpreterFor (Process a (Either Text a) !! ProcessError) r
interpretProcessCerealSocket :: forall a (r :: EffectRow).
(Serialize a,
 Members
   '[Reader NvimSocket, Error BootError, Log, Resource, Race, Async,
     Embed IO]
   r) =>
ProcessOptions
-> InterpreterFor (Process a (Either Text a) !! ProcessError) r
interpretProcessCerealSocket ProcessOptions
options Sem ((Process a (Either Text a) !! ProcessError) : r) a
sem =
  (Handle -> Sem r a) -> Sem r a
forall (r :: EffectRow) a.
Members
  '[Reader NvimSocket, Resource, Error BootError, Embed IO] r =>
(Handle -> Sem r a) -> Sem r a
withSocket \ Handle
handle ->
    Sem (ProcessOutput 'Stdout (Either Text a) : r) a -> Sem r a
forall a (r :: EffectRow).
Serialize a =>
InterpreterFor (ProcessOutput 'Stdout (Either Text a)) r
interpretProcessOutputCereal (Sem (ProcessOutput 'Stdout (Either Text a) : r) a -> Sem r a)
-> Sem (ProcessOutput 'Stdout (Either Text a) : r) a -> Sem r a
forall a b. (a -> b) -> a -> b
$
    Sem (ProcessInput a : ProcessOutput 'Stdout (Either Text a) : r) a
-> Sem (ProcessOutput 'Stdout (Either Text a) : r) a
forall a (r :: EffectRow).
Serialize a =>
InterpreterFor (ProcessInput a) r
interpretProcessInputCereal (Sem (ProcessInput a : ProcessOutput 'Stdout (Either Text a) : r) a
 -> Sem (ProcessOutput 'Stdout (Either Text a) : r) a)
-> Sem
     (ProcessInput a : ProcessOutput 'Stdout (Either Text a) : r) a
-> Sem (ProcessOutput 'Stdout (Either Text a) : r) a
forall a b. (a -> b) -> a -> b
$
    ProcessOptions
-> Handle
-> Handle
-> InterpreterFor
     (Process a (Either Text a) !! ProcessError)
     (ProcessInput a : ProcessOutput 'Stdout (Either Text a) : r)
forall i o (r :: EffectRow).
Members
  '[ProcessInput i, ProcessOutput 'Stdout o, Resource, Race, Async,
    Embed IO]
  r =>
ProcessOptions
-> Handle
-> Handle
-> InterpreterFor (Process i o !! ProcessError) r
interpretProcessHandles ProcessOptions
options Handle
handle Handle
handle (Sem ((Process a (Either Text a) !! ProcessError) : r) a
-> Sem
     ((Process a (Either Text a) !! ProcessError)
        : ProcessInput a : ProcessOutput 'Stdout (Either Text a) : r)
     a
forall (e2 :: (* -> *) -> * -> *) (e3 :: (* -> *) -> * -> *)
       (e1 :: (* -> *) -> * -> *) (r :: EffectRow) a.
Sem (e1 : r) a -> Sem (e1 : e2 : e3 : r) a
raiseUnder2 Sem ((Process a (Either Text a) !! ProcessError) : r) a
sem)