-- |
-- Module      : Streamly.Internal.FileSystem.DirOptions
-- Copyright   : (c) 2024 Composewell Technologies
--
-- License     : BSD3
-- Maintainer  : streamly@composewell.com
-- Portability : GHC

module Streamly.Internal.FileSystem.DirOptions
    (
      ReadOptions (..)
    , followSymlinks
    , ignoreMissing
    , ignoreSymlinkLoops
    , ignoreInaccessible
    , defaultReadOptions
    )
where

-- NOTE: If we are following symlinks, then we want to determine the type of
-- the link destination not the link itself, so we need to use stat instead of
-- lstat for resolving the symlink.
--
-- For recursive traversal, instead of classifying the dirents using stat, we
-- can leave them unclassified, and deal with ENOTDIR when doing an opendir. We
-- can just ignore that error if it is not a dir. This way we do not need to do
-- stat at all. Or we can basically say don't try to determine the type of
-- symlinks and always try to read symlinks as dirs. We can have an option for
-- classifying symlinks or DT_UNKNOWN as potential dirs.

-- When resolving a symlink we may encounter errors only if the directory entry
-- is a symlink. If the directory entry is not a symlink then stat on it will
-- have permissions, it will not give ELOOP or ENOENT unless the file was
-- deleted or recreated after we read the dirent.

-- | Options controlling the behavior of directory read.
data ReadOptions =
    ReadOptions
    { ReadOptions -> Bool
_followSymlinks :: Bool
    , ReadOptions -> Bool
_ignoreELOOP :: Bool
    , ReadOptions -> Bool
_ignoreENOENT :: Bool
    , ReadOptions -> Bool
_ignoreEACCESS :: Bool
    }

-- | Control how symbolic links are handled when determining the type
-- of a directory entry.
--
-- * If set to 'True', symbolic links are resolved before classification.
--   This means a symlink pointing to a directory will be treated as a
--   directory, and a symlink pointing to a file will be treated as a
--   non-directory.
--
-- * If set to 'False', all symbolic links are classified as non-directories,
--   without attempting to resolve their targets.
--
-- Enabling resolution may cause additional errors to occur due to
-- insufficient permissions, broken links, or symlink loops. Such errors
-- can be ignored or handled using the appropriate options.
--
-- The default is 'False'.
--
-- On Windows this option has no effect as of now, symlinks are not followed to
-- determine the type.
followSymlinks :: Bool -> ReadOptions -> ReadOptions
followSymlinks :: Bool -> ReadOptions -> ReadOptions
followSymlinks Bool
x ReadOptions
opts = ReadOptions
opts {_followSymlinks = x}

-- | When the 'followSymlinks' option is enabled and a directory entry is a
-- symbolic link, we resolve it to determine the type of the symlink target.
-- This option controls the behavior when encountering symlink loop errors
-- during resolution.
--
-- When set to 'True', symlink loop errors are ignored, and the type of the
-- entry is reported as not a directory. When set to 'False', the directory
-- read operation fails with an error.
--
-- The default is 'True'.
--
-- On Windows this option has no effect as of now, symlinks are not followed to
-- determine the type.
ignoreSymlinkLoops :: Bool -> ReadOptions -> ReadOptions
ignoreSymlinkLoops :: Bool -> ReadOptions -> ReadOptions
ignoreSymlinkLoops Bool
x ReadOptions
opts = ReadOptions
opts {_ignoreELOOP = x}

-- | When the 'followSymlinks' option is enabled and a directory entry is a
-- symbolic link, we resolve it to determine the type of the symlink target.
-- This option controls the behavior when encountering broken symlink errors
-- during resolution.
--
-- When set to 'True', broken symlink errors are ignored, and the type of the
-- entry is reported as not a directory. When set to 'False', the directory
-- read operation fails with an error.
--
-- The default is 'True'.
--
-- On Windows this option has no effect as of now, symlinks are not followed to
-- determine the type.
ignoreMissing :: Bool -> ReadOptions -> ReadOptions
ignoreMissing :: Bool -> ReadOptions -> ReadOptions
ignoreMissing Bool
x ReadOptions
opts = ReadOptions
opts {_ignoreENOENT = x}

-- | When the 'followSymlinks' option is enabled and a directory entry is a
-- symbolic link, we resolve it to determine the type of the symlink target.
-- This option controls the behavior when encountering permission errors
-- during resolution.
--
-- When set to 'True', any permission errors are ignored, and the type of the
-- entry is reported as not a directory. When set to 'False', the directory
-- read operation fails with an error.
--
-- The default is 'True'.
--
-- On Windows this option has no effect as of now, symlinks are not followed to
-- determine the type.
ignoreInaccessible :: Bool -> ReadOptions -> ReadOptions
ignoreInaccessible :: Bool -> ReadOptions -> ReadOptions
ignoreInaccessible Bool
x ReadOptions
opts = ReadOptions
opts {_ignoreEACCESS = x}

-- XXX find ignores errors when following symlinks, by default.
-- NOTE: The defaultReadOptions emulates the behaviour of "find".
--
defaultReadOptions :: ReadOptions
defaultReadOptions :: ReadOptions
defaultReadOptions =
    ReadOptions
    { _followSymlinks :: Bool
_followSymlinks = Bool
False
    , _ignoreELOOP :: Bool
_ignoreELOOP = Bool
True
    , _ignoreENOENT :: Bool
_ignoreENOENT = Bool
True
    , _ignoreEACCESS :: Bool
_ignoreEACCESS = Bool
True
    }