{-|
Copyright   : (c) Hisaket VioletRed, 2022
License     : AGPL-3.0-or-later
Maintainer  : hisaket@outlook.jp
Stability   : experimental
Portability : POSIX

The behavior about file access mode conforms 'System.IO.IOMode' and 'System.IO.openFile'.
-}

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use camelCase" #-}

module Polysemy.FS.Scoped
    ( module Polysemy.FS.Scoped
    , ScopedFile
    , sendBundle_
    , runScopedFile
    )
    where

import Polysemy.Path ( Path, File )
import Polysemy.Bundle ( injBundle, Bundle )
import Polysemy
    ( Member
    , Sem
    , raise2Under
    , raiseUnder
    , subsume
    , InterpretersFor
    , rewrite
    , transform
    )
import Polysemy.Scoped.Path ( ScopedP, scopedP )
import Polysemy.FS.Scoped.Internal
    ( ScopedFile (ScopedFile), sendBundle_, runScopedFile, unScopedFile )
import Polysemy.Input ( Input )
import Polysemy.Internal.Sing ( KnownList )
import Polysemy.Internal.Kind ( Append )
import Control.Category ((>>>))


scopedFile
    ::  accessMode format es b r handle a
    .   (Member (ScopedFile (Mode format accessMode) es b handle) r, KnownList es)
    =>  Path b File -> Sem (Append es (Bundle es ': Input (Path b File) ': r)) a -> Sem r a
scopedFile :: Path b File
-> Sem (Append es (Bundle es : Input (Path b File) : r)) a
-> Sem r a
scopedFile Path b File
path = Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format)
       (es :: [(* -> *) -> * -> *]) b (r :: [(* -> *) -> * -> *]) handle.
Member (ScopedFile (Mode format accessMode) es b handle) r =>
Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
scopedFile_bundle Path b File
path (Sem (Bundle es : Input (Path b File) : r) a -> Sem r a)
-> (Sem (Append es (Bundle es : Input (Path b File) : r)) a
    -> Sem (Bundle es : Input (Path b File) : r) a)
-> Sem (Append es (Bundle es : Input (Path b File) : r)) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Sem (Append es (Bundle es : Input (Path b File) : r)) a
-> Sem (Bundle es : Input (Path b File) : r) a
forall (es :: [(* -> *) -> * -> *]) (r :: [(* -> *) -> * -> *]) a.
KnownList es =>
Sem (Append es (Bundle es : r)) a -> Sem (Bundle es : r) a
sendBundle_

scopedFile_single
    :: accessMode format e es b r handle
    .  (Member (ScopedFile (Mode format accessMode) es b handle) r, Member e es)
    => Path b File -> InterpretersFor '[e, Input (Path b File)] r
scopedFile_single :: Path b File -> InterpretersFor '[e, Input (Path b File)] r
scopedFile_single Path b File
path = Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format)
       (es :: [(* -> *) -> * -> *]) b (r :: [(* -> *) -> * -> *]) handle.
Member (ScopedFile (Mode format accessMode) es b handle) r =>
Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
scopedFile_bundle Path b File
path (Sem (Bundle es : Input (Path b File) : r) a -> Sem r a)
-> (Sem (e : Input (Path b File) : r) a
    -> Sem (Bundle es : Input (Path b File) : r) a)
-> Sem (e : Input (Path b File) : r) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall (rInitial :: [(* -> *) -> * -> *]) x.
 e (Sem rInitial) x -> Bundle es (Sem rInitial) x)
-> Sem (e : Input (Path b File) : r) a
-> Sem (Bundle es : Input (Path b File) : r) a
forall (e1 :: (* -> *) -> * -> *) (e2 :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e1 (Sem rInitial) x -> e2 (Sem rInitial) x)
-> Sem (e1 : r) a -> Sem (e2 : r) a
rewrite forall (rInitial :: [(* -> *) -> * -> *]) x.
e (Sem rInitial) x -> Bundle es (Sem rInitial) x
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *])
       (m :: * -> *) a.
Member e r =>
e m a -> Bundle r m a
injBundle

scopedFile_bundle
    :: accessMode format es b r handle
    .  Member (ScopedFile (Mode format accessMode) es b handle) r
    => Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
scopedFile_bundle :: Path b File -> InterpretersFor '[Bundle es, Input (Path b File)] r
scopedFile_bundle Path b File
path = (forall (rInitial :: [(* -> *) -> * -> *]) x.
 ScopedP (Path b File) handle (Bundle es) (Sem rInitial) x
 -> ScopedFile
      (Mode format accessMode) es b handle (Sem rInitial) x)
-> Sem (ScopedP (Path b File) handle (Bundle es) : r) a -> Sem r a
forall (e1 :: (* -> *) -> * -> *) (e2 :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
Member e2 r =>
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e1 (Sem rInitial) x -> e2 (Sem rInitial) x)
-> Sem (e1 : r) a -> Sem r a
transform forall (rInitial :: [(* -> *) -> * -> *]) x.
ScopedP (Path b File) handle (Bundle es) (Sem rInitial) x
-> ScopedFile (Mode format accessMode) es b handle (Sem rInitial) x
forall k k (mode :: k) (es :: [(k -> *) -> k -> *]) b handle
       (m :: k -> *) (a :: k).
ScopedP (Path b File) handle (Bundle es) m a
-> ScopedFile mode es b handle m a
ScopedFile (Sem (ScopedP (Path b File) handle (Bundle es) : r) a -> Sem r a)
-> (Sem (Bundle es : Input (Path b File) : r) a
    -> Sem (ScopedP (Path b File) handle (Bundle es) : r) a)
-> Sem (Bundle es : Input (Path b File) : r) a
-> Sem r a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path b File
-> InterpretersFor
     '[Bundle es, Input (Path b File)]
     (ScopedP (Path b File) handle (Bundle es) : r)
forall path resource (effect :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]).
Member (ScopedP path resource effect) r =>
path -> InterpretersFor '[effect, Input path] r
scopedP Path b File
path (Sem
   (Bundle es
      : Input (Path b File) : ScopedP (Path b File) handle (Bundle es)
      : r)
   a
 -> Sem (ScopedP (Path b File) handle (Bundle es) : r) a)
-> (Sem (Bundle es : Input (Path b File) : r) a
    -> Sem
         (Bundle es
            : Input (Path b File) : ScopedP (Path b File) handle (Bundle es)
            : r)
         a)
-> Sem (Bundle es : Input (Path b File) : r) a
-> Sem (ScopedP (Path b File) handle (Bundle es) : r) a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Sem (Bundle es : Input (Path b File) : r) a
-> Sem
     (Bundle es
        : Input (Path b File) : ScopedP (Path b File) handle (Bundle es)
        : r)
     a
forall (e3 :: (* -> *) -> * -> *) (e1 :: (* -> *) -> * -> *)
       (e2 :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
Sem (e1 : e2 : r) a -> Sem (e1 : e2 : e3 : r) a
raise2Under


{- | A tag type that represents file open mode in POSIX.
-}
data Mode (fmt :: Format) (acc :: AccessMode)

data Format =
        TextFormat
    |   BytesFormat

data AccessMode =
      ReadAccess
    -- ^read only access mode ('System.IO.ReadMode')

    | WriteAccess
    -- ^write only access mode ('System.IO.WriteMode')

    | AppendAccess
    -- ^append access mode ('System.IO.AppendMode')

    | RwAccess
    -- ^read write access mode ('System.IO.ReadWriteMode')

-- | A type signature of interpreters for scoped file access.
type Access format accessMode es r b =
        a
    .   (handle. Sem (ScopedFile (Mode format accessMode) es b handle ': r) a)
    ->  Sem r a


rewriteScopedFile
    ::  (Sem (ScopedP (Path b File) handle (Bundle es) ': r) a -> Sem (ScopedP (Path b' File) handle' (Bundle es') ': r) a)
    ->  Sem (ScopedFile mode es b handle ': r) a
    ->  Sem (ScopedFile mode es' b' handle' ': r) a
rewriteScopedFile :: (Sem (ScopedP (Path b File) handle (Bundle es) : r) a
 -> Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a)
-> Sem (ScopedFile mode es b handle : r) a
-> Sem (ScopedFile mode es' b' handle' : r) a
rewriteScopedFile Sem (ScopedP (Path b File) handle (Bundle es) : r) a
-> Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a
f = (forall (rInitial :: [(* -> *) -> * -> *]) x.
 ScopedFile mode es b handle (Sem rInitial) x
 -> ScopedP (Path b File) handle (Bundle es) (Sem rInitial) x)
-> Sem (ScopedFile mode es b handle : r) a
-> Sem (ScopedP (Path b File) handle (Bundle es) : r) a
forall (e1 :: (* -> *) -> * -> *) (e2 :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e1 (Sem rInitial) x -> e2 (Sem rInitial) x)
-> Sem (e1 : r) a -> Sem (e2 : r) a
rewrite forall (rInitial :: [(* -> *) -> * -> *]) x.
ScopedFile mode es b handle (Sem rInitial) x
-> ScopedP (Path b File) handle (Bundle es) (Sem rInitial) x
forall k (mode :: k) k (es :: [(k -> *) -> k -> *]) b handle
       (m :: k -> *) (a :: k).
ScopedFile mode es b handle m a
-> ScopedP (Path b File) handle (Bundle es) m a
unScopedFile (Sem (ScopedFile mode es b handle : r) a
 -> Sem (ScopedP (Path b File) handle (Bundle es) : r) a)
-> (Sem (ScopedP (Path b File) handle (Bundle es) : r) a
    -> Sem (ScopedFile mode es' b' handle' : r) a)
-> Sem (ScopedFile mode es b handle : r) a
-> Sem (ScopedFile mode es' b' handle' : r) a
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> Sem (ScopedP (Path b File) handle (Bundle es) : r) a
-> Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a
f (Sem (ScopedP (Path b File) handle (Bundle es) : r) a
 -> Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a)
-> (Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a
    -> Sem (ScopedFile mode es' b' handle' : r) a)
-> Sem (ScopedP (Path b File) handle (Bundle es) : r) a
-> Sem (ScopedFile mode es' b' handle' : r) a
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (forall (rInitial :: [(* -> *) -> * -> *]) x.
 ScopedP (Path b' File) handle' (Bundle es') (Sem rInitial) x
 -> ScopedFile mode es' b' handle' (Sem rInitial) x)
-> Sem (ScopedP (Path b' File) handle' (Bundle es') : r) a
-> Sem (ScopedFile mode es' b' handle' : r) a
forall (e1 :: (* -> *) -> * -> *) (e2 :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
(forall (rInitial :: [(* -> *) -> * -> *]) x.
 e1 (Sem rInitial) x -> e2 (Sem rInitial) x)
-> Sem (e1 : r) a -> Sem (e2 : r) a
rewrite forall (rInitial :: [(* -> *) -> * -> *]) x.
ScopedP (Path b' File) handle' (Bundle es') (Sem rInitial) x
-> ScopedFile mode es' b' handle' (Sem rInitial) x
forall k k (mode :: k) (es :: [(k -> *) -> k -> *]) b handle
       (m :: k -> *) (a :: k).
ScopedP (Path b File) handle (Bundle es) m a
-> ScopedFile mode es b handle m a
ScopedFile

transformerToRewriter
    ::  (Sem (e0 ': e1 ': r) a -> Sem (e1 ': r) a)
    ->  Sem (e0 ': r) a -> Sem (e1 ': r) a
transformerToRewriter :: (Sem (e0 : e1 : r) a -> Sem (e1 : r) a)
-> Sem (e0 : r) a -> Sem (e1 : r) a
transformerToRewriter Sem (e0 : e1 : r) a -> Sem (e1 : r) a
f = Sem (e0 : r) a -> Sem (e0 : e1 : r) a
forall (e2 :: (* -> *) -> * -> *) (e1 :: (* -> *) -> * -> *)
       (r :: [(* -> *) -> * -> *]) a.
Sem (e1 : r) a -> Sem (e1 : e2 : r) a
raiseUnder (Sem (e0 : r) a -> Sem (e0 : e1 : r) a)
-> (Sem (e0 : e1 : r) a -> Sem (e1 : r) a)
-> Sem (e0 : r) a
-> Sem (e1 : r) a
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> Sem (e0 : e1 : r) a -> Sem (e1 : r) a
f

rewriterToTransformer
    ::  (r. Sem (e0 ': r) a -> Sem (e1 ': r) a)
    ->  (r. Member e1 r' => Sem (e0 ': r') a -> Sem r' a)
rewriterToTransformer :: (forall (r :: [(* -> *) -> * -> *]).
 Sem (e0 : r) a -> Sem (e1 : r) a)
-> forall (r :: k). Member e1 r' => Sem (e0 : r') a -> Sem r' a
rewriterToTransformer forall (r :: [(* -> *) -> * -> *]).
Sem (e0 : r) a -> Sem (e1 : r) a
f = Sem (e0 : r') a -> Sem (e1 : r') a
forall (r :: [(* -> *) -> * -> *]).
Sem (e0 : r) a -> Sem (e1 : r) a
f (Sem (e0 : r') a -> Sem (e1 : r') a)
-> (Sem (e1 : r') a -> Sem r' a) -> Sem (e0 : r') a -> Sem r' a
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> Sem (e1 : r') a -> Sem r' a
forall (e :: (* -> *) -> * -> *) (r :: [(* -> *) -> * -> *]) a.
Member e r =>
Sem (e : r) a -> Sem r a
subsume