packages feed

atelier-core-0.6.1.0: src/Atelier/Effects/FileSystem/Glob.hs

module Atelier.Effects.FileSystem.Glob
    ( -- * Effect
      Glob (..)
    , globDir
    , globDir1
    , glob
    , globDirWith

      -- * Re-exports from 'System.FilePath.Glob'.
    , module GlobExports

      -- * Interpreters
    , runIO
    , runScripted
    , GlobScript (..)
    )
where

import Effectful (Effect, IOE)
import Effectful.Dispatch.Dynamic (interpret_, reinterpret)
import Effectful.State.Static.Shared (evalState, get, put)
import Effectful.TH (makeEffect)
import System.FilePath.Glob (GlobOptions, Pattern)

import System.FilePath.Glob qualified as Glob
import System.FilePath.Glob qualified as GlobExports hiding (glob, globDir, globDir1, globDirWith)


data Glob :: Effect where
    -- | Lifted 'Glob.globDir'.
    GlobDir :: [Pattern] -> FilePath -> Glob m [[FilePath]]
    -- | Lifted 'Glob.globDir1'.
    GlobDir1 :: Pattern -> FilePath -> Glob m [FilePath]
    -- | Lifted 'Glob.glob'.
    Glob :: String -> Glob m [FilePath]
    -- | Lifted 'Glob.globDirWith'.
    GlobDirWith :: GlobOptions -> [Pattern] -> FilePath -> Glob m ([[FilePath]], Maybe [FilePath])


makeEffect ''Glob


runIO :: (IOE :> es) => Eff (Glob : es) a -> Eff es a
runIO = interpret_ \case
    GlobDir patterns filePath -> liftIO $ Glob.globDir patterns filePath
    GlobDir1 pattern filePath -> liftIO $ Glob.globDir1 pattern filePath
    Glob pattern -> liftIO $ Glob.glob pattern
    GlobDirWith opts patterns filePath -> liftIO $ Glob.globDirWith opts patterns filePath


-- | Script element for the test interpreter.
data GlobScript
    = -- | Return this result for the next 'globDir' call.
      NextGlobDir [[FilePath]]
    | -- | Return this result for the next 'globDir1' call.
      NextGlobDir1 [FilePath]
    | -- | Return this result for the next 'glob' call.
      NextGlob [FilePath]
    | -- | Return this result for the next 'globDirWith' call.
      NextGlobDirWith ([[FilePath]], Maybe [FilePath])


-- | Scripted interpreter for testing. Pops the next matching entry off the
-- queue for each call; does not require 'IOE'.
runScripted :: [GlobScript] -> Eff (Glob : es) a -> Eff es a
runScripted script = reinterpret (evalState script) \_ -> \case
    GlobDir _ _ ->
        get >>= \case
            NextGlobDir r : rest -> put rest >> pure r
            _ -> error "GlobScripted: expected NextGlobDir but queue was empty or mismatched"
    GlobDir1 _ _ ->
        get >>= \case
            NextGlobDir1 r : rest -> put rest >> pure r
            _ -> error "GlobScripted: expected NextGlobDir1 but queue was empty or mismatched"
    Glob _ ->
        get >>= \case
            NextGlob r : rest -> put rest >> pure r
            _ -> error "GlobScripted: expected NextGlob but queue was empty or mismatched"
    GlobDirWith _ _ _ ->
        get >>= \case
            NextGlobDirWith r : rest -> put rest >> pure r
            _ -> error "GlobScripted: expected NextGlobDirWith but queue was empty or mismatched"