-- | Adapted from how Shelly does finding in Shelly.Find
-- (shelly is BSD-licensed)

module System.FSNotify.Find where

import Control.Monad
import Control.Monad.IO.Class
import System.Directory (doesDirectoryExist, listDirectory, pathIsSymbolicLink)
import System.FilePath

find :: Bool -> FilePath -> IO [FilePath]
find :: Bool -> FilePath -> IO [FilePath]
find Bool
followSymlinks = Bool -> [FilePath] -> FilePath -> IO [FilePath]
find' Bool
followSymlinks  []

find' :: Bool -> [FilePath] -> FilePath -> IO [FilePath]
find' :: Bool -> [FilePath] -> FilePath -> IO [FilePath]
find' Bool
followSymlinks [FilePath]
startValue FilePath
dir = do
  (rPaths, aPaths) <- FilePath -> IO ([FilePath], [FilePath])
lsRelAbs FilePath
dir
  foldM visit startValue (zip rPaths aPaths)
  where
    visit :: [FilePath] -> (FilePath, FilePath) -> IO [FilePath]
visit [FilePath]
acc (FilePath
relativePath, FilePath
absolutePath) = do
      isDir <- IO Bool -> IO Bool
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ FilePath -> IO Bool
doesDirectoryExist FilePath
absolutePath
      sym <- liftIO $ pathIsSymbolicLink absolutePath
      let newAcc = FilePath
relativePath FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
acc
      if isDir && (followSymlinks || not sym)
        then find' followSymlinks newAcc relativePath
        else return newAcc

lsRelAbs :: FilePath -> IO ([FilePath], [FilePath])
lsRelAbs :: FilePath -> IO ([FilePath], [FilePath])
lsRelAbs FilePath
fp = do
  files <- IO [FilePath] -> IO [FilePath]
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO [FilePath] -> IO [FilePath]) -> IO [FilePath] -> IO [FilePath]
forall a b. (a -> b) -> a -> b
$ FilePath -> IO [FilePath]
listDirectory FilePath
fp
  let absolute = (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath
fp FilePath -> FilePath -> FilePath
</>) [FilePath]
files
  let relativized = (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (\FilePath
p -> [FilePath] -> FilePath
joinPath [FilePath
fp, FilePath
p]) [FilePath]
files
  return (relativized, absolute)