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

Unscoped file access and interop with polysemy-fs package.
-}

{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use camelCase" #-}
{-# LANGUAGE PartialTypeSignatures #-}

module Polysemy.FS.Scoped.Oneshot where

import Polysemy ( Member, Sem, Members, interpret )
import Polysemy.FS.Scoped
    ( ScopedFile
    , AccessMode (ReadAccess, AppendAccess, WriteAccess)
    , Format (TextFormat, BytesFormat)
    , Mode
    , scopedFile_single
    )
import qualified Polysemy.SequentialAccess.Text as SAT
import qualified Polysemy.SequentialAccess.ByteString as SAB
import qualified Polysemy.SequentialAccess as SA
import Polysemy.Path
    ( toFilePath
    , parseSomeFile
    , Path
    , Abs
    , File
    , PathException
    , Rel
    , SomeBase
    )
import Polysemy.Error ( Error )
import Control.Category ( (>>>) )
import qualified Data.ByteString as BS
import Path ( SomeBase (..) )
import Prelude hiding ( readFile, appendFile )
import qualified Polysemy.FS.Scoped.Text as ST
import qualified Polysemy.FS.Scoped.ByteString as SB
import Polysemy.FS
    ( FSRead (ReadFileBS, ReadFileUtf8)
    , FSWrite (WriteFileBS, WriteFileUtf8)
    )

readFile
    ::  (Member (ScopedFile (Mode fmt ReadAccess) es b handle) r, Member (SA.Read SA.ToEnd i) es)
    =>  Path b File -> Sem r i
readFile :: Path b File -> Sem r i
readFile Path b File
path = Path b File
-> InterpretersFor '[Read ToEnd i, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format) (e :: Effect)
       (es :: [Effect]) b (r :: [Effect]) 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
path (Sem (Append '[Read ToEnd i, Input (Path b File)] r) i -> Sem r i)
-> Sem (Append '[Read ToEnd i, Input (Path b File)] r) i -> Sem r i
forall a b. (a -> b) -> a -> b
$ ToEnd -> Sem (Read ToEnd i : Input (Path b File) : r) i
forall sz i (r :: [Effect]). Member (Read sz i) r => sz -> Sem r i
SA.read ToEnd
SA.ToEnd

overwriteFile
    ::  (Member (ScopedFile (Mode fmt WriteAccess) es b handle) r, Member (SA.Overwrite o) es)
    =>  Path b File -> o -> Sem r ()
overwriteFile :: Path b File -> o -> Sem r ()
overwriteFile Path b File
path o
o = Path b File
-> InterpretersFor '[Overwrite o, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format) (e :: Effect)
       (es :: [Effect]) b (r :: [Effect]) 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
path (Sem (Append '[Overwrite o, Input (Path b File)] r) () -> Sem r ())
-> Sem (Append '[Overwrite o, Input (Path b File)] r) ()
-> Sem r ()
forall a b. (a -> b) -> a -> b
$ o -> Sem (Overwrite o : Input (Path b File) : r) ()
forall o (r :: [Effect]). Member (Overwrite o) r => o -> Sem r ()
SA.overwrite o
o

extendFile
    ::  (Member (ScopedFile (Mode fmt WriteAccess) es b handle) r, Member (SA.Extend o) es)
    =>  Path b File -> o -> Sem r ()
extendFile :: Path b File -> o -> Sem r ()
extendFile Path b File
path o
o = Path b File -> InterpretersFor '[Extend o, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format) (e :: Effect)
       (es :: [Effect]) b (r :: [Effect]) 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
path (Sem (Append '[Extend o, Input (Path b File)] r) () -> Sem r ())
-> Sem (Append '[Extend o, Input (Path b File)] r) () -> Sem r ()
forall a b. (a -> b) -> a -> b
$ o -> Sem (Extend o : Input (Path b File) : r) ()
forall o (r :: [Effect]). Member (Extend o) r => o -> Sem r ()
SA.extend o
o

appendFile
    ::  (Member (ScopedFile (Mode fmt AppendAccess) es b handle) r, Member (SA.Append o) es)
    =>  Path b File -> o -> Sem r ()
appendFile :: Path b File -> o -> Sem r ()
appendFile Path b File
path o
o = Path b File -> InterpretersFor '[Append o, Input (Path b File)] r
forall (accessMode :: AccessMode) (format :: Format) (e :: Effect)
       (es :: [Effect]) b (r :: [Effect]) 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
path (Sem (Append '[Append o, Input (Path b File)] r) () -> Sem r ())
-> Sem (Append '[Append o, Input (Path b File)] r) () -> Sem r ()
forall a b. (a -> b) -> a -> b
$ o -> Sem (Append o : Input (Path b File) : r) ()
forall o (r :: [Effect]). Member (Append o) r => o -> Sem r ()
SA.append o
o

readFile_single
    ::  Member (ScopedFile (Mode fmt ReadAccess) '[SA.Read SA.ToEnd i] b handle) r
    =>  Path b File -> Sem r i
readFile_single :: Path b File -> Sem r i
readFile_single = Path b File -> Sem r i
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) i.
(Member (ScopedFile (Mode fmt 'ReadAccess) es b handle) r,
 Member (Read ToEnd i) es) =>
Path b File -> Sem r i
readFile

overwriteFile_single
    ::  Member (ScopedFile (Mode fmt WriteAccess) '[SA.Overwrite o] b handle) r
    =>  Path b File -> o -> Sem r ()
overwriteFile_single :: Path b File -> o -> Sem r ()
overwriteFile_single = Path b File -> o -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Overwrite o) es) =>
Path b File -> o -> Sem r ()
overwriteFile

extendFile_single
    ::  Member (ScopedFile (Mode fmt WriteAccess) '[SA.Extend o] b handle) r
    =>  Path b File -> o -> Sem r ()
extendFile_single :: Path b File -> o -> Sem r ()
extendFile_single = Path b File -> o -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Extend o) es) =>
Path b File -> o -> Sem r ()
extendFile

appendFile_single
    ::  Member (ScopedFile (Mode fmt AppendAccess) '[SA.Append o] b handle) r
    =>  Path b File -> o -> Sem r ()
appendFile_single :: Path b File -> o -> Sem r ()
appendFile_single = Path b File -> o -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'AppendAccess) es b handle) r,
 Member (Append o) es) =>
Path b File -> o -> Sem r ()
appendFile



{- | Example:

>>> import Polysemy.Resource ( resourceToIO )
>>> import qualified Polysemy.FS.Scoped.Text as ST
>>> import qualified Polysemy.FS.Scoped.ByteString as SB
>>> import Polysemy.Scoped.Path ( weakenScopedP )
>>> import Polysemy.FS.Scoped ( rewriteScopedFile, transformerToRewriter )
>>> import Polysemy ( runFinal, embedToFinal, subsume_ )
>>> import Polysemy.Error ( errorToIOFinal )
>>> :{
    fsReadToIO :: Sem '[FSRead] a -> IO (Either PathException a)
    fsReadToIO m =
        runFinal $ embedToFinal $ resourceToIO $ errorToIOFinal
            $   ST.readAccessToIO $ rewriteScopedFile (transformerToRewriter $ weakenScopedP @'[SAT.ReadToEnd] @_ @(Path Abs _))
            $   ST.readAccessToIO $ rewriteScopedFile (transformerToRewriter $ weakenScopedP @'[SAT.ReadToEnd] @_ @(Path Rel _))
            $   SB.readAccessToIO $ rewriteScopedFile (transformerToRewriter $ weakenScopedP @'[SAB.ReadToEnd] @_ @(Path Abs _))
            $   SB.readAccessToIO $ rewriteScopedFile (transformerToRewriter $ weakenScopedP @'[SAB.ReadToEnd] @_ @(Path Rel _))
            $   fsReadToScopedRead
            $   subsume_ m
:}

-}

fsReadToScopedRead
    ::  handle0 handle1 handle2 handle3 r a
    .   Members
            '[  ScopedFile (Mode BytesFormat ReadAccess) '[SAB.ReadToEnd] Abs handle0
            ,   ScopedFile (Mode BytesFormat ReadAccess) '[SAB.ReadToEnd] Rel handle1
            ,   ScopedFile (Mode TextFormat ReadAccess) '[SAT.ReadToEnd] Abs handle2
            ,   ScopedFile (Mode TextFormat ReadAccess) '[SAT.ReadToEnd] Rel handle3
            ,   Error PathException
            ] r
    =>  Sem (FSRead ': r) a
    ->  Sem r a
fsReadToScopedRead :: Sem (FSRead : r) a -> Sem r a
fsReadToScopedRead = (forall (rInitial :: [Effect]) x.
 FSRead (Sem rInitial) x -> Sem r x)
-> Sem (FSRead : r) a -> Sem r a
forall (e :: Effect) (r :: [Effect]) a.
FirstOrder e "interpret" =>
(forall (rInitial :: [Effect]) x. e (Sem rInitial) x -> Sem r x)
-> Sem (e : r) a -> Sem r a
interpret \case
    ReadFileBS path ->
        Path b File -> Sem r (SomeBase File)
forall (r :: [Effect]) b.
Members '[Error PathException] r =>
Path b File -> Sem r (SomeBase File)
toSomeBase Path b File
path Sem r (SomeBase File) -> (SomeBase File -> Sem r x) -> Sem r x
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Abs Path Abs File
path -> Path Abs File -> Sem r x
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) i.
(Member (ScopedFile (Mode fmt 'ReadAccess) es b handle) r,
 Member (Read ToEnd i) es) =>
Path b File -> Sem r i
readFile @BytesFormat Path Abs File
path
            Rel Path Rel File
path -> Path Rel File -> Sem r x
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) i.
(Member (ScopedFile (Mode fmt 'ReadAccess) es b handle) r,
 Member (Read ToEnd i) es) =>
Path b File -> Sem r i
readFile @BytesFormat Path Rel File
path
    ReadFileUtf8 path ->
        Path b File -> Sem r (SomeBase File)
forall (r :: [Effect]) b.
Members '[Error PathException] r =>
Path b File -> Sem r (SomeBase File)
toSomeBase Path b File
path Sem r (SomeBase File) -> (SomeBase File -> Sem r x) -> Sem r x
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Abs Path Abs File
path -> Path Abs File -> Sem r x
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) i.
(Member (ScopedFile (Mode fmt 'ReadAccess) es b handle) r,
 Member (Read ToEnd i) es) =>
Path b File -> Sem r i
readFile @TextFormat Path Abs File
path
            Rel Path Rel File
path -> Path Rel File -> Sem r x
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) i.
(Member (ScopedFile (Mode fmt 'ReadAccess) es b handle) r,
 Member (Read ToEnd i) es) =>
Path b File -> Sem r i
readFile @TextFormat Path Rel File
path

fsWriteToScopedWrite
    ::  handle0 handle1 handle2 handle3 r a
    .   Members
            '[  ScopedFile (Mode BytesFormat WriteAccess) '[SAB.Overwrite] Abs handle0
            ,   ScopedFile (Mode BytesFormat WriteAccess) '[SAB.Overwrite] Rel handle1
            ,   ScopedFile (Mode TextFormat WriteAccess) '[SAT.Extend] Abs handle2
            ,   ScopedFile (Mode TextFormat WriteAccess) '[SAT.Extend] Rel handle3
            ,   Error PathException
            ] r
    =>  Sem (FSWrite ': r) a
    ->  Sem r a
fsWriteToScopedWrite :: Sem (FSWrite : r) a -> Sem r a
fsWriteToScopedWrite = (forall (rInitial :: [Effect]) x.
 FSWrite (Sem rInitial) x -> Sem r x)
-> Sem (FSWrite : r) a -> Sem r a
forall (e :: Effect) (r :: [Effect]) a.
FirstOrder e "interpret" =>
(forall (rInitial :: [Effect]) x. e (Sem rInitial) x -> Sem r x)
-> Sem (e : r) a -> Sem r a
interpret \case
    WriteFileBS path content ->
        Path b File -> Sem r (SomeBase File)
forall (r :: [Effect]) b.
Members '[Error PathException] r =>
Path b File -> Sem r (SomeBase File)
toSomeBase Path b File
path Sem r (SomeBase File) -> (SomeBase File -> Sem r ()) -> Sem r ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Abs Path Abs File
path -> Path Abs File -> ByteString -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Overwrite o) es) =>
Path b File -> o -> Sem r ()
overwriteFile @BytesFormat Path Abs File
path ByteString
content
            Rel Path Rel File
path -> Path Rel File -> ByteString -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Overwrite o) es) =>
Path b File -> o -> Sem r ()
overwriteFile @BytesFormat Path Rel File
path ByteString
content
    WriteFileUtf8 path content ->
        Path b File -> Sem r (SomeBase File)
forall (r :: [Effect]) b.
Members '[Error PathException] r =>
Path b File -> Sem r (SomeBase File)
toSomeBase Path b File
path Sem r (SomeBase File) -> (SomeBase File -> Sem r ()) -> Sem r ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Abs Path Abs File
path -> Path Abs File -> Text -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Extend o) es) =>
Path b File -> o -> Sem r ()
extendFile @TextFormat Path Abs File
path Text
content
            Rel Path Rel File
path -> Path Rel File -> Text -> Sem r ()
forall (fmt :: Format) (es :: [Effect]) b handle (r :: [Effect]) o.
(Member (ScopedFile (Mode fmt 'WriteAccess) es b handle) r,
 Member (Extend o) es) =>
Path b File -> o -> Sem r ()
extendFile @TextFormat Path Rel File
path Text
content

toSomeBase :: Members '[Error PathException] r => Path b File -> Sem r (SomeBase File)
toSomeBase :: Path b File -> Sem r (SomeBase File)
toSomeBase = Path b File -> FilePath
forall b t. Path b t -> FilePath
toFilePath (Path b File -> FilePath)
-> (FilePath -> Sem r (SomeBase File))
-> Path b File
-> Sem r (SomeBase File)
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> FilePath -> Sem r (SomeBase File)
forall (r :: [Effect]).
Members '[Error PathException] r =>
FilePath -> Sem r (SomeBase File)
parseSomeFile
-- Perhaps, an error can't occur definitely so the error effect can be removed...