packages feed

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

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

module System.FilePath.FilePather.Find(
  findAllFiles
, always
, findFilesAlways
, findFilesBase
, findFilesBaseAlways
) where

import Control.Applicative ( Applicative(liftA2, pure) )
import Control.Category ( Category(id) )
import Control.Lens ( view )
import Control.Monad ( join, Monad((>>=)) )
import Control.Monad.Reader.Class ( MonadReader(ask) )
import System.FilePath.FilePather.Posix
    ( (</>), FilePath, dropTrailingPathSeparator )
import System.IO ( IO )
import Data.Bool ( Bool(True), bool )
import Data.Function(($))
import Data.Functor ( Functor(fmap) )
import Data.Traversable ( Traversable(traverse) )
import Data.Semigroup ( Semigroup((<>)) )
import System.Directory(doesDirectoryExist, listDirectory)
import System.FilePath.FilePather.ReadFilePath
    ( ReadFilePathT(..), readFilePath )

findAllFiles ::
  ReadFilePathT IO Bool
  -> ReadFilePathT IO [FilePath]
findAllFiles (ReadFilePathT test) =
  let bool' ::
        Monad f =>
        f a
        -> f a
        -> f Bool
        -> f a
      bool' f t p =
        p >>= bool f t
      partitionM ::
        Applicative m =>
        (a -> m Bool)
        -> [a]
        -> m ([a], [a])
      partitionM _ [] =
        pure ([], [])
      partitionM f (x:xs) =
        liftA2
          (\res (as, bs) ->
            let onres p q =
                  bool p q res
            in  (onres id (x:) as, onres (x:) id bs)
          )
          (f x)
          (partitionM f xs)
      findFiles' base dx =
        bool'
          (pure [])
          (
            let findFiles'' dir =
                  let dir' =
                        base </> dir
                  in  do  (dirs,files) <- listDirectory dir' >>= partitionM (\d -> doesDirectoryExist (dir' </> d))
                          rest <- fmap join (traverse (\d -> findFiles'' (dir </> d)) dirs)
                          pure (fmap (dir </>) files <> rest)
            in  findFiles'' dx
          )
          (test $ view readFilePath dropTrailingPathSeparator dx)
  in  ReadFilePathT (`findFiles'` "")

always ::
  Applicative f =>
  ReadFilePathT f Bool
always =
  pure True

findFilesAlways ::
  ReadFilePathT IO [FilePath]
findFilesAlways =
  findAllFiles always

findFilesBase ::
  ReadFilePathT IO Bool
  -> ReadFilePathT IO [FilePath]
findFilesBase r =
  do  p <- ask
      z <- findAllFiles r
      pure (fmap (p </>) z)

findFilesBaseAlways ::
  ReadFilePathT IO [FilePath]
findFilesBaseAlways =
  findFilesBase always