packages feed

skeletest-0.4.0: src/Skeletest/Internal/Paths.hs

{-# LANGUAGE LambdaCase #-}

module Skeletest.Internal.Paths (
  setOriginalDirectory,
  readTestFile,
  listTestFiles,
) where

import Control.Monad (forM)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Text (Text)
import Data.Text.IO qualified as Text
import Skeletest.Internal.Error (invariantViolation)
import System.Directory (doesDirectoryExist, getCurrentDirectory, listDirectory)
import System.Environment (lookupEnv)
import System.FilePath ((</>))
import System.IO.Unsafe (unsafePerformIO)

originalDirectoryRef :: IORef FilePath
originalDirectoryRef = unsafePerformIO $ newIORef (invariantViolation "Original directory not set")
{-# NOINLINE originalDirectoryRef #-}

data TestRoot = TestRootBuildDir | TestRootCWD | TestRoot FilePath

setOriginalDirectory :: FilePath -> IO ()
setOriginalDirectory buildDir = do
  testRoot <-
    lookupEnv "SKELETEST_TEST_ROOT" >>= \case
      Nothing -> pure TestRootBuildDir
      Just "BUILD_DIR" -> pure TestRootBuildDir
      Just "CWD" -> pure TestRootCWD
      Just fp -> pure $ TestRoot fp
  root <-
    case testRoot of
      TestRootBuildDir -> pure buildDir
      TestRootCWD -> getCurrentDirectory
      TestRoot fp -> pure fp
  writeIORef originalDirectoryRef root

readTestFile :: FilePath -> IO Text
readTestFile fp = do
  dir <- readIORef originalDirectoryRef
  Text.readFile $ dir </> fp

listTestFiles :: IO [FilePath]
listTestFiles = do
  dir <- readIORef originalDirectoryRef
  listDirectoryRecursive dir
 where
  listDirectoryRecursive dir = do
    entries <- filter (`notElem` ignoredDirs) <$> listDirectory dir
    fmap concat . forM entries $ \entry -> do
      let absEntry = dir </> entry
      isDir <- doesDirectoryExist absEntry
      if isDir
        then map (entry </>) <$> listDirectoryRecursive absEntry
        else pure [entry]

  -- Hardcode some paths to ignore
  ignoredDirs =
    [ ".git"
    , "dist-newstyle"
    , ".stack-work"
    ]