-- | A potentially too simple interface for getting candidates for search from file trees. Doesn't follow symbolic links. For a better solution to this use
-- [unix-recursive](https://hackage.haskell.org/package/unix-recursive).
module Talash.Files (-- * Types
                     Conf (..) , FindConf (..) , FindInDirs (..) , FileTree (..)
                     -- * File Collection
                    , defConf , withExts , ignoreExts , findWithExts , findFilesInDirs , executables
                     -- * Internal Details
                    , dirContentsWith , fileTreeWith , minify , flatten , ext) where

import Control.Exception
import qualified Data.ByteString.Char8 as B
import qualified Data.HashSet as S
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Vector as V
import Intro
import System.Posix.Directory.ByteString
import System.Posix.Env.ByteString
import System.Posix.Files.ByteString

-- Configruation for the search when recursivley constructing the file tree.
data Conf = Conf {
              -- | Test for whether to include a file in the file tree. The second argument is the base name of the file.
              Conf -> FileStatus -> ByteString -> IO Bool
includeFile :: FileStatus -> ByteString -> IO Bool ,
              -- | Test used to determine whether to enter a directory to search for files.
              Conf -> ByteString -> Bool
filterPath :: ByteString -> Bool }

-- | A simple type to represent a search either for a specific set of extensions or esle for excluding a specific set of extensions. An extension here
-- is just the part of the filename after the last '.' i.e this module doesn't handle multiple extensions.
data FindConf = Find !(S.HashSet ByteString) | Ignore !(S.HashSet ByteString) deriving Int -> FindConf -> ShowS
[FindConf] -> ShowS
FindConf -> String
(Int -> FindConf -> ShowS)
-> (FindConf -> String) -> ([FindConf] -> ShowS) -> Show FindConf
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FindConf] -> ShowS
$cshowList :: [FindConf] -> ShowS
show :: FindConf -> String
$cshow :: FindConf -> String
showsPrec :: Int -> FindConf -> ShowS
$cshowsPrec :: Int -> FindConf -> ShowS
Show


data FindInDirs = FindInDirs {
                    -- | The configuration of finding or excluding the extensions for this set of directories.
                    FindInDirs -> FindConf
confLocal :: FindConf ,
                    -- | The list of directories to which this configuration should apply.
                    FindInDirs -> [ByteString]
dirsLocal :: [ByteString]}

data FileTree a = Dir { FileTree a -> a
rootDir :: a -- ^ The root directory
                      , FileTree a -> Vector a
dirFiles :: (V.Vector a) -- ^ The files in the root directory that are not subdirectories
                      , FileTree a -> Vector (FileTree a)
subDirs :: (V.Vector (FileTree a))} -- ^ The vector of trees formed by subdirectories
                        deriving (FileTree a -> FileTree a -> Bool
(FileTree a -> FileTree a -> Bool)
-> (FileTree a -> FileTree a -> Bool) -> Eq (FileTree a)
forall a. Eq a => FileTree a -> FileTree a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FileTree a -> FileTree a -> Bool
$c/= :: forall a. Eq a => FileTree a -> FileTree a -> Bool
== :: FileTree a -> FileTree a -> Bool
$c== :: forall a. Eq a => FileTree a -> FileTree a -> Bool
Eq , Int -> FileTree a -> ShowS
[FileTree a] -> ShowS
FileTree a -> String
(Int -> FileTree a -> ShowS)
-> (FileTree a -> String)
-> ([FileTree a] -> ShowS)
-> Show (FileTree a)
forall a. Show a => Int -> FileTree a -> ShowS
forall a. Show a => [FileTree a] -> ShowS
forall a. Show a => FileTree a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FileTree a] -> ShowS
$cshowList :: forall a. Show a => [FileTree a] -> ShowS
show :: FileTree a -> String
$cshow :: forall a. Show a => FileTree a -> String
showsPrec :: Int -> FileTree a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> FileTree a -> ShowS
Show)

-- | Default configuration, include every file and search directory.
defConf :: Conf
defConf :: Conf
defConf = (FileStatus -> ByteString -> IO Bool)
-> (ByteString -> Bool) -> Conf
Conf ((ByteString -> IO Bool) -> FileStatus -> ByteString -> IO Bool
forall a b. a -> b -> a
const ((ByteString -> IO Bool) -> FileStatus -> ByteString -> IO Bool)
-> (IO Bool -> ByteString -> IO Bool)
-> IO Bool
-> FileStatus
-> ByteString
-> IO Bool
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. IO Bool -> ByteString -> IO Bool
forall a b. a -> b -> a
const (IO Bool -> FileStatus -> ByteString -> IO Bool)
-> IO Bool -> FileStatus -> ByteString -> IO Bool
forall a b. (a -> b) -> a -> b
$ Bool -> IO Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True) (Bool -> ByteString -> Bool
forall a b. a -> b -> a
const Bool
True)

-- | Given the configuration and a directory returns a vector where the Left elements are the files in the directory that pass the `includeFile` test while
--   the Right elements are subdirectories that pass the `filterPath` test.
{-# INLINEABLE dirContentsWith #-}
dirContentsWith :: Conf -> ByteString -> IO (V.Vector (Either ByteString ByteString))
dirContentsWith :: Conf -> ByteString -> IO (Vector (Either ByteString ByteString))
dirContentsWith Conf
c ByteString
d = IO DirStream
-> (DirStream -> IO ())
-> (DirStream -> IO (Vector (Either ByteString ByteString)))
-> IO (Vector (Either ByteString ByteString))
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (ByteString -> IO DirStream
openDirStream ByteString
d) DirStream -> IO ()
closeDirStream ((DirStream -> IO (Maybe (Either ByteString ByteString, DirStream)))
-> DirStream -> IO (Vector (Either ByteString ByteString))
forall (m :: * -> *) b a.
Monad m =>
(b -> m (Maybe (a, b))) -> b -> m (Vector a)
V.unfoldrM (\DirStream
s -> (Either ByteString ByteString
 -> Maybe (Either ByteString ByteString, DirStream))
-> IO (Either ByteString ByteString)
-> IO (Maybe (Either ByteString ByteString, DirStream))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map (\Either ByteString ByteString
d -> if Either ByteString ByteString
d Either ByteString ByteString
-> Either ByteString ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString -> Either ByteString ByteString
forall a b. a -> Either a b
Left ByteString
"" then Maybe (Either ByteString ByteString, DirStream)
forall a. Maybe a
Nothing else (Either ByteString ByteString, DirStream)
-> Maybe (Either ByteString ByteString, DirStream)
forall a. a -> Maybe a
Just (Either ByteString ByteString
d , DirStream
s)) (IO (Either ByteString ByteString)
 -> IO (Maybe (Either ByteString ByteString, DirStream)))
-> (DirStream -> IO (Either ByteString ByteString))
-> DirStream
-> IO (Maybe (Either ByteString ByteString, DirStream))
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. DirStream -> IO (Either ByteString ByteString)
go (DirStream -> IO (Maybe (Either ByteString ByteString, DirStream)))
-> DirStream
-> IO (Maybe (Either ByteString ByteString, DirStream))
forall a b. (a -> b) -> a -> b
$ DirStream
s))
  where
    go :: DirStream -> IO (Either ByteString ByteString)
go DirStream
s = ByteString -> IO (Either ByteString ByteString)
nm (ByteString -> IO (Either ByteString ByteString))
-> IO ByteString -> IO (Either ByteString ByteString)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< DirStream -> IO ByteString
readDirStream DirStream
s
      where
        nm :: ByteString -> IO (Either ByteString ByteString)
nm ByteString
f
          | ByteString
f ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
""                                         = Either ByteString ByteString -> IO (Either ByteString ByteString)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Either ByteString ByteString
forall a b. a -> Either a b
Left ByteString
"")
          | Bool
otherwise                                       = FileStatus -> IO (Either ByteString ByteString)
hr (FileStatus -> IO (Either ByteString ByteString))
-> IO FileStatus -> IO (Either ByteString ByteString)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ByteString -> IO FileStatus
getSymbolicLinkStatus ByteString
f
          where
            hr :: FileStatus -> IO (Either ByteString ByteString)
hr FileStatus
fs
              | FileStatus -> Bool
isDirectory FileStatus
fs                              = IO (Either ByteString ByteString)
hd
              | Bool
otherwise                                   = IO Bool
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a
ifM (Conf -> FileStatus -> ByteString -> IO Bool
includeFile Conf
c FileStatus
fs ByteString
f) (Either ByteString ByteString -> IO (Either ByteString ByteString)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either ByteString ByteString -> IO (Either ByteString ByteString))
-> (ByteString -> Either ByteString ByteString)
-> ByteString
-> IO (Either ByteString ByteString)
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Either ByteString ByteString
forall a b. a -> Either a b
Left (ByteString -> IO (Either ByteString ByteString))
-> ByteString -> IO (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
f) (DirStream -> IO (Either ByteString ByteString)
go DirStream
s)
            hd :: IO (Either ByteString ByteString)
hd
              | Conf -> ByteString -> Bool
filterPath Conf
c ByteString
f Bool -> Bool -> Bool
&& ByteString
f ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= ByteString
"." Bool -> Bool -> Bool
&& ByteString
f ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= ByteString
".."     = IO Bool
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
-> IO (Either ByteString ByteString)
forall (m :: * -> *) a. Monad m => m Bool -> m a -> m a -> m a
ifM (ByteString -> Bool -> Bool -> Bool -> IO Bool
fileAccess ByteString
f Bool
True Bool
False Bool
False) (Either ByteString ByteString -> IO (Either ByteString ByteString)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either ByteString ByteString -> IO (Either ByteString ByteString))
-> (ByteString -> Either ByteString ByteString)
-> ByteString
-> IO (Either ByteString ByteString)
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Either ByteString ByteString
forall a b. b -> Either a b
Right (ByteString -> IO (Either ByteString ByteString))
-> ByteString -> IO (Either ByteString ByteString)
forall a b. (a -> b) -> a -> b
$ ByteString
f) (DirStream -> IO (Either ByteString ByteString)
go DirStream
s)
              | Bool
otherwise                                   = DirStream -> IO (Either ByteString ByteString)
go DirStream
s

-- | Constructs the file tree with the given the second argument at the root according to the given configuration.
{-# INLINEABLE fileTreeWith #-}
fileTreeWith :: Conf -> ByteString -> IO (FileTree Text)
fileTreeWith :: Conf -> ByteString -> IO (FileTree Text)
fileTreeWith Conf
c ByteString
d = IO ByteString
-> (ByteString -> IO ())
-> (ByteString -> IO (FileTree Text))
-> IO (FileTree Text)
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket IO ByteString
getWorkingDirectory ByteString -> IO ()
changeWorkingDirectory (IO (FileTree Text) -> ByteString -> IO (FileTree Text)
forall a b. a -> b -> a
const (IO (FileTree Text) -> ByteString -> IO (FileTree Text))
-> IO (FileTree Text) -> ByteString -> IO (FileTree Text)
forall a b. (a -> b) -> a -> b
$ ByteString -> IO ()
changeWorkingDirectory ByteString
d IO () -> IO (FileTree Text) -> IO (FileTree Text)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Vector (Either ByteString ByteString) -> IO (FileTree Text)
go (Vector (Either ByteString ByteString) -> IO (FileTree Text))
-> IO (Vector (Either ByteString ByteString)) -> IO (FileTree Text)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Conf -> ByteString -> IO (Vector (Either ByteString ByteString))
dirContentsWith Conf
c ByteString
"."))
  where
    go :: Vector (Either ByteString ByteString) -> IO (FileTree Text)
go Vector (Either ByteString ByteString)
v = (\(Vector Text
a , Vector ByteString
b) -> Text -> Vector Text -> Vector (FileTree Text) -> FileTree Text
forall a. a -> Vector a -> Vector (FileTree a) -> FileTree a
Dir (ByteString -> Text
T.decodeUtf8 ByteString
d) Vector Text
a (Vector (FileTree Text) -> FileTree Text)
-> IO (Vector (FileTree Text)) -> IO (FileTree Text)
forall (m :: * -> *) a b. Monad m => (a -> b) -> m a -> m b
<$!> (ByteString -> IO (FileTree Text))
-> Vector ByteString -> IO (Vector (FileTree Text))
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Vector a -> m (Vector b)
V.mapM (Conf -> ByteString -> IO (FileTree Text)
fileTreeWith Conf
c) Vector ByteString
b) ((Vector Text, Vector ByteString) -> IO (FileTree Text))
-> (Vector (Either ByteString ByteString)
    -> (Vector Text, Vector ByteString))
-> Vector (Either ByteString ByteString)
-> IO (FileTree Text)
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (Either ByteString ByteString -> Either Text ByteString)
-> Vector (Either ByteString ByteString)
-> (Vector Text, Vector ByteString)
forall a b c. (a -> Either b c) -> Vector a -> (Vector b, Vector c)
V.partitionWith ((ByteString -> Text)
-> Either ByteString ByteString -> Either Text ByteString
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ByteString -> Text
T.decodeUtf8) (Vector (Either ByteString ByteString) -> IO (FileTree Text))
-> Vector (Either ByteString ByteString) -> IO (FileTree Text)
forall a b. (a -> b) -> a -> b
$ Vector (Either ByteString ByteString)
v

-- | Collapses the directories with only subdirectory and no other files.
{-# INLINEABLE minify #-}
minify :: FileTree Text -> FileTree Text
minify :: FileTree Text -> FileTree Text
minify (Dir Text
d Vector Text
f Vector (FileTree Text)
t)
  | Vector Text
f Vector Text -> Vector Text -> Bool
forall a. Eq a => a -> a -> Bool
== Vector Text
forall a. Vector a
V.empty Bool -> Bool -> Bool
&& Vector (FileTree Text) -> Int
forall a. Vector a -> Int
V.length Vector (FileTree Text)
t Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1  = (\(Dir Text
d' Vector Text
f' Vector (FileTree Text)
t') -> Text -> Vector Text -> Vector (FileTree Text) -> FileTree Text
forall a. a -> Vector a -> Vector (FileTree a) -> FileTree a
Dir (Text
d Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
d') Vector Text
f' Vector (FileTree Text)
t') (Vector (FileTree Text) -> FileTree Text
forall a. Vector a -> a
V.unsafeHead Vector (FileTree Text)
t)
  | Bool
otherwise                        = Text -> Vector Text -> Vector (FileTree Text) -> FileTree Text
forall a. a -> Vector a -> Vector (FileTree a) -> FileTree a
Dir Text
d Vector Text
f (Vector (FileTree Text) -> FileTree Text)
-> (Vector (FileTree Text) -> Vector (FileTree Text))
-> Vector (FileTree Text)
-> FileTree Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (FileTree Text -> FileTree Text)
-> Vector (FileTree Text) -> Vector (FileTree Text)
forall a b. (a -> b) -> Vector a -> Vector b
V.map FileTree Text -> FileTree Text
minify (Vector (FileTree Text) -> FileTree Text)
-> Vector (FileTree Text) -> FileTree Text
forall a b. (a -> b) -> a -> b
$ Vector (FileTree Text)
t

-- | Flattens the fileTree by completing the paths of the file relative to that of root directory.
{-# INLINEABLE flatten #-}
flatten :: FileTree Text -> V.Vector Text
flatten :: FileTree Text -> Vector Text
flatten (Dir Text
d Vector Text
f Vector (FileTree Text)
t) = (FileTree Text -> Vector Text)
-> Vector (FileTree Text) -> Vector Text
forall a b. (a -> Vector b) -> Vector a -> Vector b
V.concatMap FileTree Text -> Vector Text
go Vector (FileTree Text)
t Vector Text -> Vector Text -> Vector Text
forall a. Semigroup a => a -> a -> a
<> (Text -> Text) -> Vector Text -> Vector Text
forall a b. (a -> b) -> Vector a -> Vector b
V.map ((Text
d Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) Vector Text
f
  where
    go :: FileTree Text -> Vector Text
go (Dir Text
d' !Vector Text
f' Vector (FileTree Text)
t') = FileTree Text -> Vector Text
flatten (Text -> Vector Text -> Vector (FileTree Text) -> FileTree Text
forall a. a -> Vector a -> Vector (FileTree a) -> FileTree a
Dir (Text
d Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
d') Vector Text
f' Vector (FileTree Text)
t')

{-# INLINABLE withExts #-}
withExts :: [ByteString] -- ^ The set of extensions to search for
  -> FindConf
withExts :: [ByteString] -> FindConf
withExts = HashSet ByteString -> FindConf
Find (HashSet ByteString -> FindConf)
-> ([ByteString] -> HashSet ByteString) -> [ByteString] -> FindConf
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [ByteString] -> HashSet ByteString
forall a. (Eq a, Hashable a) => [a] -> HashSet a
S.fromList

{-# INLINABLE ignoreExts #-}
ignoreExts :: [ByteString] -- ^ The set of extensions to ignore.
  -> FindConf
ignoreExts :: [ByteString] -> FindConf
ignoreExts = HashSet ByteString -> FindConf
Ignore (HashSet ByteString -> FindConf)
-> ([ByteString] -> HashSet ByteString) -> [ByteString] -> FindConf
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [ByteString] -> HashSet ByteString
forall a. (Eq a, Hashable a) => [a] -> HashSet a
S.fromList

-- | The last extension of a file. Returns empty bytestring if there is none.
{-# INLINABLE ext #-}
ext :: ByteString -> ByteString
ext :: ByteString -> ByteString
ext ByteString
c = if ByteString
e ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
c then ByteString
forall a. Monoid a => a
mempty else ByteString
e
  where
    e :: ByteString
e = (ByteString, ByteString) -> ByteString
forall a b. (a, b) -> b
snd ((ByteString, ByteString) -> ByteString)
-> (ByteString -> (ByteString, ByteString))
-> ByteString
-> ByteString
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (Char -> Bool) -> ByteString -> (ByteString, ByteString)
B.spanEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'.') (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
c

-- | Find files in the given set of directories that either have a specific extension (`Find` case) or else excluding a certain set of extensiosn (`Ignore` case).
{-# INLINE findWithExts #-}
findWithExts :: FindInDirs -> IO (V.Vector (FileTree Text))
findWithExts :: FindInDirs -> IO (Vector (FileTree Text))
findWithExts (FindInDirs FindConf
c [ByteString]
d) = (ByteString -> IO (FileTree Text))
-> Vector ByteString -> IO (Vector (FileTree Text))
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Vector a -> m (Vector b)
V.mapM (Conf -> ByteString -> IO (FileTree Text)
fileTreeWith Conf
ch) (Vector ByteString -> IO (Vector (FileTree Text)))
-> ([ByteString] -> Vector ByteString)
-> [ByteString]
-> IO (Vector (FileTree Text))
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [ByteString] -> Vector ByteString
forall a. [a] -> Vector a
V.fromList ([ByteString] -> IO (Vector (FileTree Text)))
-> [ByteString] -> IO (Vector (FileTree Text))
forall a b. (a -> b) -> a -> b
$ [ByteString]
d
  where
    ch :: Conf
ch
      | Find   HashSet ByteString
es <- FindConf
c     = Conf
defConf {includeFile :: FileStatus -> ByteString -> IO Bool
includeFile = \ !FileStatus
s !ByteString
n -> Bool -> IO Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ FileStatus -> Bool
isRegularFile FileStatus
s Bool -> Bool -> Bool
&& ByteString -> HashSet ByteString -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
S.member (ByteString -> ByteString
ext ByteString
n) HashSet ByteString
es}
      | Ignore HashSet ByteString
es <- FindConf
c     = Conf
defConf {includeFile :: FileStatus -> ByteString -> IO Bool
includeFile = \ !FileStatus
s !ByteString
n -> Bool -> IO Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ FileStatus -> Bool
isRegularFile FileStatus
s Bool -> Bool -> Bool
&& Bool -> Bool
not (ByteString -> HashSet ByteString -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
S.member (ByteString -> ByteString
ext ByteString
n) HashSet ByteString
es)}


-- | Like `findWithExts` but applied to mutliple lists of directories each with their own configuration of extensions.
{-# INLINABLE findFilesInDirs #-}
findFilesInDirs :: [FindInDirs] -> IO (V.Vector (FileTree Text))
findFilesInDirs :: [FindInDirs] -> IO (Vector (FileTree Text))
findFilesInDirs = (FindInDirs
 -> IO (Vector (FileTree Text)) -> IO (Vector (FileTree Text)))
-> IO (Vector (FileTree Text))
-> [FindInDirs]
-> IO (Vector (FileTree Text))
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\FindInDirs
a IO (Vector (FileTree Text))
t -> IO (Vector (FileTree Text))
t IO (Vector (FileTree Text))
-> IO (Vector (FileTree Text)) -> IO (Vector (FileTree Text))
forall a. Semigroup a => a -> a -> a
<> FindInDirs -> IO (Vector (FileTree Text))
findWithExts FindInDirs
a) (Vector (FileTree Text) -> IO (Vector (FileTree Text))
forall (f :: * -> *) a. Applicative f => a -> f a
pure Vector (FileTree Text)
forall a. Monoid a => a
mempty)

-- | Find all the executables in PATH
executables :: IO (V.Vector Text)
executables :: IO (Vector Text)
executables = (ByteString -> IO (Vector Text) -> IO (Vector Text))
-> IO (Vector Text) -> [ByteString] -> IO (Vector Text)
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\ByteString
a IO (Vector Text)
t -> IO (Vector Text)
t IO (Vector Text) -> IO (Vector Text) -> IO (Vector Text)
forall a. Semigroup a => a -> a -> a
<> (FileTree Text -> Vector Text)
-> IO (FileTree Text) -> IO (Vector Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map ((Text -> Text) -> Vector Text -> Vector Text
forall a b. (a -> b) -> Vector a -> Vector b
V.map ((Char -> Bool) -> Text -> Text
T.takeWhileEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'/')) (Vector Text -> Vector Text)
-> (FileTree Text -> Vector Text) -> FileTree Text -> Vector Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. FileTree Text -> Vector Text
flatten) (Conf -> ByteString -> IO (FileTree Text)
fileTreeWith Conf
cl ByteString
a)) (Vector Text -> IO (Vector Text)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Vector Text
forall a. Vector a
V.empty) ([ByteString] -> IO (Vector Text))
-> (ByteString -> [ByteString]) -> ByteString -> IO (Vector Text)
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Char -> ByteString -> [ByteString]
B.split Char
':' (ByteString -> IO (Vector Text))
-> IO ByteString -> IO (Vector Text)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ByteString -> ByteString -> IO ByteString
getEnvDefault ByteString
"PATH" ByteString
""
  where
    cl :: Conf
cl = Conf
defConf { filterPath :: ByteString -> Bool
filterPath = Bool -> ByteString -> Bool
forall a b. a -> b -> a
const Bool
False , includeFile :: FileStatus -> ByteString -> IO Bool
includeFile = \ FileStatus
s ByteString
p ->  (Bool -> Bool) -> IO Bool -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
map ((FileStatus -> Bool
isRegularFile FileStatus
s Bool -> Bool -> Bool
|| FileStatus -> Bool
isSymbolicLink FileStatus
s) Bool -> Bool -> Bool
&&) (IO Bool -> IO Bool) -> (Bool -> IO Bool) -> Bool -> IO Bool
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Bool -> Bool -> Bool -> IO Bool
fileAccess ByteString
p Bool
False Bool
False (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ Bool
True}