packages feed

filepather-0.5.1: src/System/FilePath/FilePather/Find.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE NoImplicitPrelude #-}

module System.FilePath.FilePather.Find(
  findWith
, findWith'
, findAllDirectories
, findAll
, findAllFiles
) where

import Control.Applicative ( Applicative(pure) )
import Control.Category ( Category((.)) )
import Control.Exception ( Exception )
import Control.Lens ( view, _Wrapped, _1, _2 )
import Control.Monad ( Monad((>>=)), filterM )
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import Control.Monad.Reader
    ( MonadReader(local, ask) )
import Data.Bool ( Bool(True), bool )
import Data.Foldable ( fold )
import Data.Functor ( Functor(fmap), (<$>) )
import Data.Maybe ( Maybe(..), catMaybes )
import Data.Semigroup ( Semigroup((<>)) )
import Data.Traversable ( Traversable(traverse) )
import System.FilePath ( FilePath, (</>) )
import System.FilePath.FilePather.Directory
    ( listDirectory, doesFileExist, doesDirectoryExist )
import System.FilePath.FilePather.ReadFilePath ( ReadFilePathT )
import System.IO ( IO )
import System.IO.Unsafe( unsafeInterleaveIO )

findWith ::
  Exception e =>
  ReadFilePathT e IO (Maybe b)
  -> ReadFilePathT e IO [b]
findWith p =
  let setl =
        local . pure
      mapMaybeM f xs =
        catMaybes <$> traverse f xs
  in  do  z <- ask
          a <- listDirectory
          let y = (z </>) <$> a
          b <- mapMaybeM (\fp -> fmap (fmap (\s -> (s, fp))) (fp `setl` p)) y
          d <- filterM (`setl` doesDirectoryExist) (fmap (view _2) b)
          let bs = fmap (view _1) b
          case d of
            [] ->
              pure bs
            _ ->
              do  n <- liftIO (unsafeInterleaveIO (fmap (>>= fold) (traverse (view _Wrapped (findWith p)) d)))
                  pure (bs <> n)
{-# INLINE findWith #-}

findWith' ::
  Exception e =>
  ReadFilePathT e IO Bool
  -> ReadFilePathT e IO [FilePath]
findWith' s =
  findWith (ask >>= \d -> fmap (bool Nothing (Just d)) s)
{-# INLINE findWith' #-}

findAllDirectories ::
  Exception e =>
  ReadFilePathT e IO [FilePath]
findAllDirectories =
  findWith' doesDirectoryExist
{-# INLINE findAllDirectories #-}

findAll ::
  Exception e =>
  ReadFilePathT e IO [FilePath]
findAll =
  findWith' (pure True)
{-# INLINE findAll #-}

findAllFiles ::
  Exception e =>
  ReadFilePathT e IO [FilePath]
findAllFiles =
  findAll >>= filterM (\p -> local (pure p) doesFileExist)
{-# INLINE findAllFiles #-}