{-# options_haddock prune #-}

-- |Description: Process Effect, Internal
module Polysemy.Process.Effect.Process where

import Polysemy.Conc.Effect.Scoped (Scoped, scoped)
import Polysemy.Input (Input (Input))
import Polysemy.Output (Output (Output))
import Polysemy.Resume (interpretResumable, restop, type (!!))
import Prelude hiding (send)

-- |Abstraction of a process with input and output.
--
-- This effect is intended to be used in a scoped manner:
--
-- @
-- import Polysemy.Resume
-- import Polysemy.Conc
-- import Polysemy.Process
-- import qualified System.Process.Typed as System
--
-- prog :: Member (Scoped resource (Process Text Text !! err)) r => Sem r Text
-- prog =
--  resumeAs "failed" do
--    withProcess do
--      send "input"
--      recv
--
-- main :: IO ()
-- main = do
--   out <- runConc $ interpretProcessNative (System.proc "cat" []) prog
--   putStrLn out
-- @
data Process i o :: Effect where
  Recv :: Process i o m o
  Send :: i -> Process i o m ()

makeSem_ ''Process

-- |Obtain a chunk of output.
recv ::
   i o r .
  Member (Process i o) r =>
  Sem r o

-- |Send data to stdin.
send ::
   i o r .
  Member (Process i o) r =>
  i ->
  Sem r ()

-- |Create a scoped resource for 'Process'.
withProcess ::
   resource i o r .
  Member (Scoped resource (Process i o)) r =>
  InterpreterFor (Process i o) r
withProcess :: forall resource i o (r :: [(* -> *) -> * -> *]).
Member (Scoped resource (Process i o)) r =>
InterpreterFor (Process i o) r
withProcess =
  forall resource (effect :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]).
Member (Scoped resource effect) r =>
InterpreterFor effect r
scoped @resource

-- |Convert 'Output' and 'Input' to 'Process'.
runProcessIO ::
   i o err r .
  Member (Process i o !! err) r =>
  InterpretersFor [Output i !! err, Input o !! err] r
runProcessIO :: forall i o err (r :: [(* -> *) -> * -> *]).
Member (Process i o !! err) r =>
InterpretersFor '[Output i !! err, Input o !! err] r
runProcessIO =
  (forall x (r0 :: [(* -> *) -> * -> *]).
 Input o (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Input o !! err) r
forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: [(* -> *) -> * -> *]).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Input o (Sem r0) x
Input ->
      forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Members '[Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop @err @(Process i o) (forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
Sem r o
recv @i @o)
  (Sem ((Input o !! err) : r) a -> Sem r a)
-> (Sem (Resumable err (Output i) : (Input o !! err) : r) a
    -> Sem ((Input o !! err) : r) a)
-> Sem (Resumable err (Output i) : (Input o !! err) : r) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  (forall x (r0 :: [(* -> *) -> * -> *]).
 Output i (Sem r0) x -> Sem (Stop err : (Input o !! err) : r) x)
-> InterpreterFor (Resumable err (Output i)) ((Input o !! err) : r)
forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: [(* -> *) -> * -> *]).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Output i
o ->
      forall err (eff :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]).
Members '[Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop @err @(Process i o) (forall i o (r :: [(* -> *) -> * -> *]).
Member (Process i o) r =>
i -> Sem r ()
send @i @o i
o)