{-# LANGUAGE RankNTypes #-}
module Data.Conduit.Filesystem
    ( sourceDirectory
    , sourceDirectoryDeep
    ) where

import Data.Conduit
import Control.Monad.Trans.Resource (MonadResource)
import Control.Monad.IO.Class (liftIO)
import System.FilePath ((</>))
import qualified Data.Streaming.Filesystem as F

-- | Stream the contents of the given directory, without traversing deeply.
--
-- This function will return /all/ of the contents of the directory, whether
-- they be files, directories, etc.
--
-- Note that the generated filepaths will be the complete path, not just the
-- filename. In other words, if you have a directory @foo@ containing files
-- @bar@ and @baz@, and you use @sourceDirectory@ on @foo@, the results will be
-- @foo/bar@ and @foo/baz@.
--
-- Since 1.1.0
sourceDirectory :: MonadResource m => FilePath -> Producer m FilePath
sourceDirectory dir =
    bracketP (F.openDirStream dir) F.closeDirStream go
  where
    go ds =
        loop
      where
        loop = do
            mfp <- liftIO $ F.readDirStream ds
            case mfp of
                Nothing -> return ()
                Just fp -> do
                    yield $ dir </> fp
                    loop

-- | Deeply stream the contents of the given directory.
--
-- This works the same as @sourceDirectory@, but will not return directories at
-- all. This function also takes an extra parameter to indicate whether
-- symlinks will be followed.
--
-- Since 1.1.0
sourceDirectoryDeep :: MonadResource m
                    => Bool -- ^ Follow directory symlinks
                    -> FilePath -- ^ Root directory
                    -> Producer m FilePath
sourceDirectoryDeep followSymlinks =
    start
  where
    start :: MonadResource m => FilePath -> Producer m FilePath
    start dir = sourceDirectory dir =$= awaitForever go

    go :: MonadResource m => FilePath -> Producer m FilePath
    go fp = do
        ft <- liftIO $ F.getFileType fp
        case ft of
            F.FTFile -> yield fp
            F.FTFileSym -> yield fp
            F.FTDirectory -> start fp
            F.FTDirectorySym
                | followSymlinks -> start fp
                | otherwise -> return ()
            F.FTOther -> return ()