packages feed

filepather-0.6.0: src/System/FilePather/FilePather.hs

{-# OPTIONS_GHC -Wall #-}

-- |
-- Module      : System.FilePather.FilePather
-- Description : Platform-native FilePath and Directory operations via Kleisli type aliases
module System.FilePather.FilePather
  ( -- * Types
    FilePather,
    FilePather',
    FilePatherA,
    FilePatherA',
    FilePatherMaybe,
    FilePatherMaybeA,
    FilePatherIO,
    FilePatherIOA,

    -- * System.FilePath re-implementations
    splitExtension,
    takeExtension,
    replaceExtension,
    dropExtension,
    addExtension,
    hasExtension,
    splitExtensions,
    takeExtensions,
    dropExtensions,
    replaceExtensions,
    isExtensionOf,
    stripExtension,
    splitFileName,
    takeFileName,
    replaceFileName,
    dropFileName,
    takeBaseName,
    replaceBaseName,
    takeDirectory,
    replaceDirectory,
    combine,
    splitPath,
    FP.joinPath,
    splitDirectories,
    splitDrive,
    joinDrive,
    takeDrive,
    hasDrive,
    dropDrive,
    isDrive,
    hasTrailingPathSeparator,
    addTrailingPathSeparator,
    dropTrailingPathSeparator,
    normalise,
    makeRelative,
    equalFilePath,
    isRelative,
    isAbsolute,
    isValid,
    makeValid,
    (</>),
    (<.>),
    (-<.>),

    -- * System.FilePath re-exports
    FP.FilePath,
    FP.pathSeparator,
    FP.pathSeparators,
    FP.isPathSeparator,
    FP.searchPathSeparator,
    FP.isSearchPathSeparator,
    FP.extSeparator,
    FP.isExtSeparator,
    FP.splitSearchPath,
    FP.getSearchPath,

    -- * System.Directory re-implementations
    createDirectory,
    createDirectoryIfMissing,
    removeDirectory,
    removeDirectoryRecursive,
    removePathForcibly,
    renameDirectory,
    listDirectory,
    getDirectoryContents,
    setCurrentDirectory,
    withCurrentDirectory,
    getXdgDirectory,
    getAppUserDataDirectory,
    removeFile,
    renameFile,
    renamePath,
    copyFile,
    copyFileWithMetadata,
    canonicalizePath,
    makeAbsolute,
    makeRelativeToCurrentDirectory,
    doesPathExist,
    doesFileExist,
    doesDirectoryExist,
    Dir.findFile,
    Dir.findFiles,
    Dir.findFileWith,
    Dir.findFilesWith,
    createFileLink,
    createDirectoryLink,
    removeDirectoryLink,
    getSymbolicLinkTarget,
    isSymbolicLink,
    pathIsSymbolicLink,
    getPermissions,
    setPermissions,
    copyPermissions,
    getAccessTime,
    getModificationTime,
    setAccessTime,
    setModificationTime,
    getFileSize,

    -- * System.Directory re-exports
    Dir.Permissions,
    Dir.emptyPermissions,
    Dir.setOwnerReadable,
    Dir.setOwnerWritable,
    Dir.setOwnerExecutable,
    Dir.setOwnerSearchable,
    Dir.XdgDirectory (..),
    Dir.XdgDirectoryList (..),
    Dir.getXdgDirectoryList,
    Dir.exeExtension,
    Dir.getCurrentDirectory,
    Dir.getHomeDirectory,
    Dir.getTemporaryDirectory,
    Dir.getUserDocumentsDirectory,
    Dir.findExecutable,
    Dir.findExecutables,
    Dir.findExecutablesInDirectories,
  )
where

import Data.Functor.Identity
import Data.Kleisli (Kleisli (..), mkKleisli')
import Data.Time.Clock (UTCTime)
import qualified System.Directory as Dir
import qualified System.FilePath as FP

-- $setup
-- >>> import Data.Functor.Identity (Identity(..))
-- >>> import Data.Kleisli (Kleisli(..))
-- >>> import qualified System.Directory as Dir
-- >>> let run (Kleisli f) x = runIdentity (f x)

type FilePather p f a = Kleisli p FilePath f a

type FilePather' p a = FilePather p Identity a

type FilePatherA f a = FilePather (->) f a

type FilePatherA' a = FilePatherA Identity a

type FilePatherMaybe p a = FilePather p Maybe a

type FilePatherMaybeA a = FilePatherMaybe (->) a

type FilePatherIO p a = FilePather p IO a

type FilePatherIOA a = FilePatherIO (->) a

------------------------------------------------------------
-- System.FilePath
------------------------------------------------------------

-- | Split a file path into the stem and extension.
--
-- >>> run splitExtension "/tmp/foo.hs"
-- ("/tmp/foo",".hs")
--
-- >>> run splitExtension "/tmp/foo"
-- ("/tmp/foo","")
--
-- >>> run splitExtension "/tmp/foo.tar.gz"
-- ("/tmp/foo.tar",".gz")
splitExtension :: FilePatherA' (String, String)
splitExtension = mkKleisli' FP.splitExtension

-- | Get the extension of a file path.
--
-- >>> run takeExtension "/tmp/foo.hs"
-- ".hs"
--
-- >>> run takeExtension "/tmp/foo"
-- ""
--
-- >>> run takeExtension "/tmp/foo.tar.gz"
-- ".gz"
--
takeExtension :: FilePatherA' String
takeExtension = mkKleisli' FP.takeExtension

-- | Replace the extension of a file path.
--
-- >>> run (replaceExtension ".md") "/tmp/foo.hs"
-- "/tmp/foo.md"
--
-- >>> run (replaceExtension ".txt") "/tmp/foo"
-- "/tmp/foo.txt"
--
-- >>> run (replaceExtension "") "/tmp/foo.hs"
-- "/tmp/foo"
replaceExtension :: String -> FilePatherA' FilePath
replaceExtension s = mkKleisli' (`FP.replaceExtension` s)

-- | Remove the extension from a file path.
--
-- >>> run dropExtension "/tmp/foo.hs"
-- "/tmp/foo"
--
-- >>> run dropExtension "/tmp/foo"
-- "/tmp/foo"
--
-- >>> run dropExtension "/tmp/foo.tar.gz"
-- "/tmp/foo.tar"
dropExtension :: FilePatherA' FilePath
dropExtension = mkKleisli' FP.dropExtension

-- | Add an extension to a file path.
--
-- >>> run (addExtension ".hs") "/tmp/foo"
-- "/tmp/foo.hs"
--
-- >>> run (addExtension ".gz") "/tmp/foo.tar"
-- "/tmp/foo.tar.gz"
--
-- >>> run (addExtension "hs") "/tmp/foo"
-- "/tmp/foo.hs"
addExtension :: String -> FilePatherA' FilePath
addExtension s = mkKleisli' (`FP.addExtension` s)

-- | Does the file path have an extension?
--
-- >>> run hasExtension "/tmp/foo.hs"
-- True
--
-- >>> run hasExtension "/tmp/foo"
-- False
--
hasExtension :: FilePatherA' Bool
hasExtension = mkKleisli' FP.hasExtension

-- | Split all extensions from a file path.
--
-- >>> run splitExtensions "/tmp/foo.tar.gz"
-- ("/tmp/foo",".tar.gz")
--
-- >>> run splitExtensions "/tmp/foo"
-- ("/tmp/foo","")
splitExtensions :: FilePatherA' (FilePath, String)
splitExtensions = mkKleisli' FP.splitExtensions

-- | Get all extensions from a file path.
--
-- >>> run takeExtensions "/tmp/foo.tar.gz"
-- ".tar.gz"
--
-- >>> run takeExtensions "/tmp/foo"
-- ""
--
-- >>> run takeExtensions "/tmp/foo.a.b.c"
-- ".a.b.c"
takeExtensions :: FilePatherA' String
takeExtensions = mkKleisli' FP.takeExtensions

-- | Drop all extensions from a file path.
--
-- >>> run dropExtensions "/tmp/foo.tar.gz"
-- "/tmp/foo"
--
-- >>> run dropExtensions "/tmp/foo"
-- "/tmp/foo"
dropExtensions :: FilePatherA' FilePath
dropExtensions = mkKleisli' FP.dropExtensions

-- | Replace all extensions of a file path.
--
-- >>> run (replaceExtensions ".txt") "/tmp/foo.tar.gz"
-- "/tmp/foo.txt"
--
-- >>> run (replaceExtensions ".a.b") "/tmp/foo.c"
-- "/tmp/foo.a.b"
replaceExtensions :: String -> FilePatherA' FilePath
replaceExtensions s = mkKleisli' (`FP.replaceExtensions` s)

-- | Is the given string an extension of the file path?
--
-- >>> run (isExtensionOf ".hs") "/tmp/foo.hs"
-- True
--
-- >>> run (isExtensionOf ".md") "/tmp/foo.hs"
-- False
--
-- >>> run (isExtensionOf "hs") "/tmp/foo.hs"
-- True
--
-- >>> run (isExtensionOf ".gz") "/tmp/foo.tar.gz"
-- True
isExtensionOf :: String -> FilePatherA' Bool
isExtensionOf s = mkKleisli' (FP.isExtensionOf s)

-- | Strip an extension from a file path, returning 'Nothing' if the
-- extension does not match.
--
-- >>> let Kleisli f = stripExtension ".hs" in f "/tmp/foo.hs"
-- Just "/tmp/foo"
--
-- >>> let Kleisli f = stripExtension ".md" in f "/tmp/foo.hs"
-- Nothing
--
-- >>> let Kleisli f = stripExtension ".gz" in f "/tmp/foo.tar.gz"
-- Just "/tmp/foo.tar"
--
-- >>> let Kleisli f = stripExtension ".tar.gz" in f "/tmp/foo.tar.gz"
-- Just "/tmp/foo"
stripExtension :: String -> FilePatherMaybeA FilePath
stripExtension s = Kleisli (FP.stripExtension s)

-- | Split a file path into directory and file components.
--
-- >>> run splitFileName "/tmp/foo.hs"
-- ("/tmp/","foo.hs")
--
-- >>> run splitFileName "/tmp/"
-- ("/tmp/","")
--
-- >>> run splitFileName "foo.hs"
-- ("./","foo.hs")
splitFileName :: FilePatherA' (String, String)
splitFileName = mkKleisli' FP.splitFileName

-- | Get the file name component of a file path.
--
-- >>> run takeFileName "/tmp/foo.hs"
-- "foo.hs"
--
-- >>> run takeFileName "/tmp/"
-- ""
--
-- >>> run takeFileName "foo.hs"
-- "foo.hs"
takeFileName :: FilePatherA' FilePath
takeFileName = mkKleisli' FP.takeFileName

-- | Replace the file name component of a file path.
--
-- >>> run (replaceFileName "bar.md") "/tmp/foo.hs"
-- "/tmp/bar.md"
--
-- >>> run (replaceFileName "baz") "/tmp/foo.hs"
-- "/tmp/baz"
replaceFileName :: String -> FilePatherA' FilePath
replaceFileName s = mkKleisli' (`FP.replaceFileName` s)

-- | Drop the file name component of a file path.
--
-- >>> run dropFileName "/tmp/foo.hs"
-- "/tmp/"
--
-- >>> run dropFileName "foo.hs"
-- "./"
dropFileName :: FilePatherA' FilePath
dropFileName = mkKleisli' FP.dropFileName

-- | Get the base name (file name without extension) of a file path.
--
-- >>> run takeBaseName "/tmp/foo.hs"
-- "foo"
--
-- >>> run takeBaseName "/tmp/foo.tar.gz"
-- "foo.tar"
--
-- >>> run takeBaseName "/tmp/"
-- ""
takeBaseName :: FilePatherA' String
takeBaseName = mkKleisli' FP.takeBaseName

-- | Replace the base name of a file path.
--
-- >>> run (replaceBaseName "bar") "/tmp/foo.hs"
-- "/tmp/bar.hs"
--
-- >>> run (replaceBaseName "quux") "/tmp/foo.tar.gz"
-- "/tmp/quux.gz"
replaceBaseName :: String -> FilePatherA' FilePath
replaceBaseName s = mkKleisli' (`FP.replaceBaseName` s)

-- | Get the directory component of a file path.
--
-- >>> run takeDirectory "/tmp/foo.hs"
-- "/tmp"
--
-- >>> run takeDirectory "/tmp/"
-- "/tmp"
--
-- >>> run takeDirectory "foo.hs"
-- "."
--
-- >>> run takeDirectory "/tmp/src/foo.hs"
-- "/tmp/src"
takeDirectory :: FilePatherA' FilePath
takeDirectory = mkKleisli' FP.takeDirectory

-- | Replace the directory component of a file path.
--
-- >>> run (replaceDirectory "/usr") "/tmp/foo.hs"
-- "/usr/foo.hs"
--
-- >>> run (replaceDirectory "src") "/tmp/foo.hs"
-- "src/foo.hs"
replaceDirectory :: String -> FilePatherA' FilePath
replaceDirectory s = mkKleisli' (`FP.replaceDirectory` s)

-- | Combine a directory path with a file path.
--
-- >>> run (combine "/tmp") "foo.hs"
-- "/tmp/foo.hs"
--
-- >>> run (combine "/tmp") "/usr/foo.hs"
-- "/usr/foo.hs"
--
-- >>> run (combine "src") "Data/Foo.hs"
-- "src/Data/Foo.hs"
combine :: FilePath -> FilePatherA' FilePath
combine s = mkKleisli' (FP.combine s)

-- | Split a file path into its path components.
--
-- >>> run splitPath "/tmp/src/foo.hs"
-- ["/","tmp/","src/","foo.hs"]
--
-- >>> run splitPath "src/foo.hs"
-- ["src/","foo.hs"]
--
-- >>> run splitPath "foo.hs"
-- ["foo.hs"]
splitPath :: FilePatherA' [FilePath]
splitPath = mkKleisli' FP.splitPath

-- | Split a file path into its directory components.
--
-- >>> run splitDirectories "/tmp/src/foo.hs"
-- ["/","tmp","src","foo.hs"]
--
-- >>> run splitDirectories "src/foo.hs"
-- ["src","foo.hs"]
--
-- >>> run splitDirectories "/"
-- ["/"]
splitDirectories :: FilePatherA' [FilePath]
splitDirectories = mkKleisli' FP.splitDirectories

-- | Split a file path into drive and remainder.
--
-- >>> run splitDrive "/tmp/foo"
-- ("/","tmp/foo")
--
-- >>> run splitDrive "tmp/foo"
-- ("","tmp/foo")
splitDrive :: FilePatherA' (FilePath, FilePath)
splitDrive = mkKleisli' FP.splitDrive

-- | Join a drive and the rest of a path.
--
-- >>> run (joinDrive "/") "tmp/foo"
-- "/tmp/foo"
--
-- >>> run (joinDrive "") "tmp/foo"
-- "tmp/foo"
joinDrive :: FilePath -> FilePatherA' FilePath
joinDrive s = mkKleisli' (FP.joinDrive s)

-- | Get the drive of a file path.
--
-- >>> run takeDrive "/tmp/foo"
-- "/"
--
-- >>> run takeDrive "tmp/foo"
-- ""
takeDrive :: FilePatherA' FilePath
takeDrive = mkKleisli' FP.takeDrive

-- | Does the file path have a drive?
--
-- >>> run hasDrive "/tmp/foo"
-- True
--
-- >>> run hasDrive "tmp/foo"
-- False
hasDrive :: FilePatherA' Bool
hasDrive = mkKleisli' FP.hasDrive

-- | Drop the drive from a file path.
--
-- >>> run dropDrive "/tmp/foo"
-- "tmp/foo"
--
-- >>> run dropDrive "tmp/foo"
-- "tmp/foo"
dropDrive :: FilePatherA' FilePath
dropDrive = mkKleisli' FP.dropDrive

-- | Is the file path a drive?
--
-- >>> run isDrive "/"
-- True
--
-- >>> run isDrive "/tmp"
-- False
isDrive :: FilePatherA' Bool
isDrive = mkKleisli' FP.isDrive

-- | Does the file path have a trailing path separator?
--
-- >>> run hasTrailingPathSeparator "/tmp/"
-- True
--
-- >>> run hasTrailingPathSeparator "/tmp"
-- False
hasTrailingPathSeparator :: FilePatherA' Bool
hasTrailingPathSeparator = mkKleisli' FP.hasTrailingPathSeparator

-- | Add a trailing path separator if one is not present.
--
-- >>> run addTrailingPathSeparator "/tmp"
-- "/tmp/"
--
-- >>> run addTrailingPathSeparator "/tmp/"
-- "/tmp/"
addTrailingPathSeparator :: FilePatherA' FilePath
addTrailingPathSeparator = mkKleisli' FP.addTrailingPathSeparator

-- | Drop the trailing path separator if present.
--
-- >>> run dropTrailingPathSeparator "/tmp/"
-- "/tmp"
--
-- >>> run dropTrailingPathSeparator "/tmp"
-- "/tmp"
--
-- >>> run dropTrailingPathSeparator "/"
-- "/"
dropTrailingPathSeparator :: FilePatherA' FilePath
dropTrailingPathSeparator = mkKleisli' FP.dropTrailingPathSeparator

-- | Normalise a file path.
--
-- >>> run normalise "/tmp//foo"
-- "/tmp/foo"
--
-- >>> run normalise "/tmp/./foo"
-- "/tmp/foo"
--
-- >>> run normalise "/tmp/foo/"
-- "/tmp/foo/"
normalise :: FilePatherA' FilePath
normalise = mkKleisli' FP.normalise

-- | Make a file path relative to a given directory.
--
-- >>> run (makeRelative "/tmp") "/tmp/foo.hs"
-- "foo.hs"
--
-- >>> run (makeRelative "/tmp") "/tmp/src/foo.hs"
-- "src/foo.hs"
--
-- >>> run (makeRelative "/usr") "/tmp/foo.hs"
-- "/tmp/foo.hs"
makeRelative :: FilePath -> FilePatherA' FilePath
makeRelative s = mkKleisli' (FP.makeRelative s)

-- | Are two file paths equal (accounting for normalisation)?
--
-- >>> run (equalFilePath "/tmp/foo") "/tmp/foo"
-- True
--
-- >>> run (equalFilePath "/tmp/foo") "/tmp/bar"
-- False
--
-- >>> run (equalFilePath "/tmp/foo") "/tmp/./foo"
-- True
equalFilePath :: FilePath -> FilePatherA' Bool
equalFilePath s = mkKleisli' (FP.equalFilePath s)

-- | Is the file path relative?
--
-- >>> run isRelative "foo/bar"
-- True
--
-- >>> run isRelative "/foo/bar"
-- False
--
-- >>> run isRelative "./foo"
-- True
isRelative :: FilePatherA' Bool
isRelative = mkKleisli' FP.isRelative

-- | Is the file path absolute?
--
-- >>> run isAbsolute "/foo/bar"
-- True
--
-- >>> run isAbsolute "foo/bar"
-- False
--
-- >>> run isAbsolute "/"
-- True
isAbsolute :: FilePatherA' Bool
isAbsolute = mkKleisli' FP.isAbsolute

-- | Is the file path valid?
--
-- >>> run isValid "/tmp/foo"
-- True
--
-- >>> run isValid ""
-- False
isValid :: FilePatherA' Bool
isValid = mkKleisli' FP.isValid

-- | Make a file path valid by replacing invalid characters.
--
-- >>> run makeValid ""
-- "_"
--
-- >>> run makeValid "/tmp/foo"
-- "/tmp/foo"
makeValid :: FilePatherA' FilePath
makeValid = mkKleisli' FP.makeValid

-- | Combine two file paths with a path separator. Synonym for 'combine'.
--
-- >>> run (combine "/tmp") "foo.hs"
-- "/tmp/foo.hs"
--
-- >>> run (combine "src") "Data/Foo.hs"
-- "src/Data/Foo.hs"
(</>) :: FilePath -> FilePatherA' FilePath
(</>) = combine

-- | Add an extension to a file path. Synonym for 'addExtension'.
--
-- >>> run (addExtension ".hs") "/tmp/foo"
-- "/tmp/foo.hs"
--
-- >>> run (addExtension ".gz") "/tmp/foo.tar"
-- "/tmp/foo.tar.gz"
(<.>) :: String -> FilePatherA' FilePath
(<.>) = addExtension

-- | Replace the extension of a file path. Synonym for 'replaceExtension'.
--
-- >>> run (replaceExtension ".md") "/tmp/foo.hs"
-- "/tmp/foo.md"
--
-- >>> run (replaceExtension ".txt") "/tmp/foo"
-- "/tmp/foo.txt"
(-<.>) :: String -> FilePatherA' FilePath
(-<.>) = replaceExtension

------------------------------------------------------------
-- System.Directory
------------------------------------------------------------

-- | Create a directory.
--
-- >>> let Kleisli f = createDirectory in f "/tmp/filepather_test_create_938271" >> Dir.removeDirectory "/tmp/filepather_test_create_938271"
createDirectory :: FilePatherIOA ()
createDirectory = Kleisli Dir.createDirectory

-- | Create a directory and any missing parent directories.
--
-- >>> let Kleisli f = createDirectoryIfMissing True in f "/tmp/filepather_test_mkdirs_938271/a/b" >> Dir.removeDirectoryRecursive "/tmp/filepather_test_mkdirs_938271"
createDirectoryIfMissing :: Bool -> FilePatherIOA ()
createDirectoryIfMissing b = Kleisli (Dir.createDirectoryIfMissing b)

-- | Remove an existing directory.
--
-- >>> Dir.createDirectory "/tmp/filepather_test_rmdir_938271"
-- >>> let Kleisli f = removeDirectory in f "/tmp/filepather_test_rmdir_938271"
removeDirectory :: FilePatherIOA ()
removeDirectory = Kleisli Dir.removeDirectory

-- | Remove a directory and all its contents recursively.
--
-- >>> Dir.createDirectoryIfMissing True "/tmp/filepather_test_rmr_938271/x/y"
-- >>> let Kleisli f = removeDirectoryRecursive in f "/tmp/filepather_test_rmr_938271"
removeDirectoryRecursive :: FilePatherIOA ()
removeDirectoryRecursive = Kleisli Dir.removeDirectoryRecursive

-- | Remove a path forcibly (file or directory, recursively).
--
-- >>> Dir.createDirectory "/tmp/filepather_test_rmf_938271"
-- >>> let Kleisli f = removePathForcibly in f "/tmp/filepather_test_rmf_938271"
removePathForcibly :: FilePatherIOA ()
removePathForcibly = Kleisli Dir.removePathForcibly

-- | Rename a directory to a new path.
--
-- >>> Dir.createDirectory "/tmp/filepather_test_rend_938271"
-- >>> let Kleisli f = renameDirectory "/tmp/filepather_test_rend2_938271" in f "/tmp/filepather_test_rend_938271"
-- >>> Dir.removeDirectory "/tmp/filepather_test_rend2_938271"
renameDirectory :: FilePath -> FilePatherIOA ()
renameDirectory dest = Kleisli (`Dir.renameDirectory` dest)

-- | List the contents of a directory (excluding @.@ and @..@).
--
-- >>> let Kleisli f = listDirectory in f "/" >>= \xs -> pure (not (null xs))
-- True
listDirectory :: FilePatherIOA [FilePath]
listDirectory = Kleisli Dir.listDirectory

-- | Get all entries in a directory (including @.@ and @..@).
--
-- >>> let Kleisli f = getDirectoryContents in f "/" >>= \xs -> pure (elem "." xs && elem ".." xs)
-- True
getDirectoryContents :: FilePatherIOA [FilePath]
getDirectoryContents = Kleisli Dir.getDirectoryContents

-- | Set the current working directory.
--
-- >>> let Kleisli f = setCurrentDirectory in Dir.getCurrentDirectory >>= \old -> f "/tmp" >> Dir.getCurrentDirectory >>= \new -> Dir.setCurrentDirectory old >> pure (new == "/tmp")
-- True
setCurrentDirectory :: FilePatherIOA ()
setCurrentDirectory = Kleisli Dir.setCurrentDirectory

-- | Run an IO action with the given directory as the working directory.
--
-- >>> let Kleisli f = withCurrentDirectory (pure ()) in f "/tmp"
withCurrentDirectory :: IO a -> FilePatherIOA a
withCurrentDirectory act = Kleisli (`Dir.withCurrentDirectory` act)

-- | Get an XDG directory for the given application.
--
-- >>> let Kleisli f = getXdgDirectory Dir.XdgConfig in f "myapp" >>= \d -> pure (not (null d))
-- True
getXdgDirectory :: Dir.XdgDirectory -> FilePatherIOA FilePath
getXdgDirectory xdg = Kleisli (Dir.getXdgDirectory xdg)

-- | Get the application user data directory.
--
-- >>> let Kleisli f = getAppUserDataDirectory in f "myapp" >>= \d -> pure (not (null d))
-- True
getAppUserDataDirectory :: FilePatherIOA FilePath
getAppUserDataDirectory = Kleisli Dir.getAppUserDataDirectory

-- | Remove a file.
--
-- >>> writeFile "/tmp/filepather_test_rm_938271" ""
-- >>> let Kleisli f = removeFile in f "/tmp/filepather_test_rm_938271"
removeFile :: FilePatherIOA ()
removeFile = Kleisli Dir.removeFile

-- | Rename a file to a new path.
--
-- >>> writeFile "/tmp/filepather_test_renf_938271" ""
-- >>> let Kleisli f = renameFile "/tmp/filepather_test_renf2_938271" in f "/tmp/filepather_test_renf_938271"
-- >>> Dir.removeFile "/tmp/filepather_test_renf2_938271"
renameFile :: FilePath -> FilePatherIOA ()
renameFile dest = Kleisli (`Dir.renameFile` dest)

-- | Rename a file or directory to a new path.
--
-- >>> writeFile "/tmp/filepather_test_renp_938271" ""
-- >>> let Kleisli f = renamePath "/tmp/filepather_test_renp2_938271" in f "/tmp/filepather_test_renp_938271"
-- >>> Dir.removeFile "/tmp/filepather_test_renp2_938271"
renamePath :: FilePath -> FilePatherIOA ()
renamePath dest = Kleisli (`Dir.renamePath` dest)

-- | Copy a file to a new path.
--
-- >>> writeFile "/tmp/filepather_test_cp_938271" ""
-- >>> let Kleisli f = copyFile "/tmp/filepather_test_cp2_938271" in f "/tmp/filepather_test_cp_938271"
-- >>> Dir.removeFile "/tmp/filepather_test_cp_938271" >> Dir.removeFile "/tmp/filepather_test_cp2_938271"
copyFile :: FilePath -> FilePatherIOA ()
copyFile dest = Kleisli (`Dir.copyFile` dest)

-- | Copy a file preserving metadata.
--
-- >>> writeFile "/tmp/filepather_test_cpm_938271" ""
-- >>> let Kleisli f = copyFileWithMetadata "/tmp/filepather_test_cpm2_938271" in f "/tmp/filepather_test_cpm_938271"
-- >>> Dir.removeFile "/tmp/filepather_test_cpm_938271" >> Dir.removeFile "/tmp/filepather_test_cpm2_938271"
copyFileWithMetadata :: FilePath -> FilePatherIOA ()
copyFileWithMetadata dest = Kleisli (`Dir.copyFileWithMetadata` dest)

-- | Canonicalize a file path (resolve symlinks and relative components).
--
-- >>> let Kleisli f = canonicalizePath in f "/" >>= \d -> pure (d == "/")
-- True
--
-- >>> let Kleisli f = canonicalizePath in f "/tmp/../tmp" >>= \d -> pure (not (null d))
-- True
canonicalizePath :: FilePatherIOA FilePath
canonicalizePath = Kleisli Dir.canonicalizePath

-- | Make a file path absolute.
--
-- >>> let Kleisli f = makeAbsolute in f "/tmp" >>= \d -> pure (d == "/tmp")
-- True
--
-- >>> let Kleisli f = makeAbsolute in f "foo" >>= \d -> pure (head d == '/')
-- True
makeAbsolute :: FilePatherIOA FilePath
makeAbsolute = Kleisli Dir.makeAbsolute

-- | Make a file path relative to the current directory.
--
-- >>> let Kleisli f = makeRelativeToCurrentDirectory in f "/" >>= \d -> pure (not (null d))
-- True
makeRelativeToCurrentDirectory :: FilePatherIOA FilePath
makeRelativeToCurrentDirectory = Kleisli Dir.makeRelativeToCurrentDirectory

-- | Does the path exist (file or directory)?
--
-- >>> let Kleisli f = doesPathExist in f "/"
-- True
--
-- >>> let Kleisli f = doesPathExist in f "/nonexistent_path_938271"
-- False
doesPathExist :: FilePatherIOA Bool
doesPathExist = Kleisli Dir.doesPathExist

-- | Does the file exist?
--
-- >>> let Kleisli f = doesFileExist in f "/nonexistent_file_938271"
-- False
doesFileExist :: FilePatherIOA Bool
doesFileExist = Kleisli Dir.doesFileExist

-- | Does the directory exist?
--
-- >>> let Kleisli f = doesDirectoryExist in f "/"
-- True
--
-- >>> let Kleisli f = doesDirectoryExist in f "/nonexistent_dir_938271"
-- False
doesDirectoryExist :: FilePatherIOA Bool
doesDirectoryExist = Kleisli Dir.doesDirectoryExist

-- | Create a file symbolic link. The first argument is the link target.
--
-- >>> Dir.removePathForcibly "/tmp/filepather_test_flink_938271"
-- >>> let Kleisli f = createFileLink "/dev/null" in f "/tmp/filepather_test_flink_938271" >> Dir.pathIsSymbolicLink "/tmp/filepather_test_flink_938271" >>= \b -> Dir.removeFile "/tmp/filepather_test_flink_938271" >> pure b
-- True
createFileLink :: FilePath -> FilePatherIOA ()
createFileLink target = Kleisli (Dir.createFileLink target)

-- | Create a directory symbolic link. The first argument is the link target.
--
-- >>> Dir.removePathForcibly "/tmp/filepather_test_dlink_938271"
-- >>> let Kleisli f = createDirectoryLink "/tmp" in f "/tmp/filepather_test_dlink_938271" >> Dir.pathIsSymbolicLink "/tmp/filepather_test_dlink_938271" >>= \b -> Dir.removeDirectoryLink "/tmp/filepather_test_dlink_938271" >> pure b
-- True
createDirectoryLink :: FilePath -> FilePatherIOA ()
createDirectoryLink target = Kleisli (Dir.createDirectoryLink target)

-- | Remove a directory symbolic link.
--
-- >>> Dir.removePathForcibly "/tmp/filepather_test_rmdl_938271"
-- >>> Dir.createDirectoryLink "/tmp" "/tmp/filepather_test_rmdl_938271"
-- >>> let Kleisli f = removeDirectoryLink in f "/tmp/filepather_test_rmdl_938271"
removeDirectoryLink :: FilePatherIOA ()
removeDirectoryLink = Kleisli Dir.removeDirectoryLink

-- | Get the target of a symbolic link.
--
-- >>> Dir.removePathForcibly "/tmp/filepather_test_gsl_938271"
-- >>> Dir.createFileLink "/dev/null" "/tmp/filepather_test_gsl_938271"
-- >>> let Kleisli f = getSymbolicLinkTarget in f "/tmp/filepather_test_gsl_938271" >>= \t -> Dir.removeFile "/tmp/filepather_test_gsl_938271" >> pure t
-- "/dev/null"
getSymbolicLinkTarget :: FilePatherIOA FilePath
getSymbolicLinkTarget = Kleisli Dir.getSymbolicLinkTarget

-- | Is the path a symbolic link?
--
-- >>> let Kleisli f = isSymbolicLink in f "/"
-- False
isSymbolicLink :: FilePatherIOA Bool
isSymbolicLink = Kleisli Dir.pathIsSymbolicLink

-- | Is the path a symbolic link? (Same as 'isSymbolicLink'.)
--
-- >>> let Kleisli f = pathIsSymbolicLink in f "/"
-- False
pathIsSymbolicLink :: FilePatherIOA Bool
pathIsSymbolicLink = Kleisli Dir.pathIsSymbolicLink

-- | Get the permissions of a file or directory.
--
-- >>> let Kleisli f = getPermissions in f "/" >>= \p -> pure (Dir.searchable p)
-- True
getPermissions :: FilePatherIOA Dir.Permissions
getPermissions = Kleisli Dir.getPermissions

-- | Set the permissions of a file or directory.
--
-- >>> writeFile "/tmp/filepather_test_sp_938271" ""
-- >>> let Kleisli f = setPermissions (Dir.setOwnerReadable True (Dir.setOwnerWritable True Dir.emptyPermissions)) in f "/tmp/filepather_test_sp_938271" >> Dir.getPermissions "/tmp/filepather_test_sp_938271" >>= \p -> Dir.removeFile "/tmp/filepather_test_sp_938271" >> pure (Dir.readable p)
-- True
setPermissions :: Dir.Permissions -> FilePatherIOA ()
setPermissions p = Kleisli (`Dir.setPermissions` p)

-- | Copy the permissions from one path to another.
--
-- >>> writeFile "/tmp/filepather_test_cpp_src_938271" ""
-- >>> writeFile "/tmp/filepather_test_cpp_938271" ""
-- >>> let Kleisli f = copyPermissions "/tmp/filepather_test_cpp_src_938271" in f "/tmp/filepather_test_cpp_938271" >> Dir.removeFile "/tmp/filepather_test_cpp_938271" >> Dir.removeFile "/tmp/filepather_test_cpp_src_938271"
copyPermissions :: FilePath -> FilePatherIOA ()
copyPermissions dest = Kleisli (`Dir.copyPermissions` dest)

-- | Get the access time of a file or directory.
--
-- >>> let Kleisli f = getAccessTime in f "/" >>= \t -> pure (t > read "2000-01-01 00:00:00 UTC")
-- True
getAccessTime :: FilePatherIOA UTCTime
getAccessTime = Kleisli Dir.getAccessTime

-- | Get the modification time of a file or directory.
--
-- >>> let Kleisli f = getModificationTime in f "/" >>= \t -> pure (t > read "2000-01-01 00:00:00 UTC")
-- True
getModificationTime :: FilePatherIOA UTCTime
getModificationTime = Kleisli Dir.getModificationTime

-- | Set the access time of a file or directory.
--
-- >>> writeFile "/tmp/filepather_test_sat_938271" ""
-- >>> let Kleisli f = setAccessTime (read "2020-06-15 12:00:00 UTC") in f "/tmp/filepather_test_sat_938271" >> Dir.getAccessTime "/tmp/filepather_test_sat_938271" >>= \t -> Dir.removeFile "/tmp/filepather_test_sat_938271" >> pure (t == read "2020-06-15 12:00:00 UTC")
-- True
setAccessTime :: UTCTime -> FilePatherIOA ()
setAccessTime t = Kleisli (`Dir.setAccessTime` t)

-- | Set the modification time of a file or directory.
--
-- >>> writeFile "/tmp/filepather_test_smt_938271" ""
-- >>> let Kleisli f = setModificationTime (read "2020-06-15 12:00:00 UTC") in f "/tmp/filepather_test_smt_938271" >> Dir.getModificationTime "/tmp/filepather_test_smt_938271" >>= \t -> Dir.removeFile "/tmp/filepather_test_smt_938271" >> pure (t == read "2020-06-15 12:00:00 UTC")
-- True
setModificationTime :: UTCTime -> FilePatherIOA ()
setModificationTime t = Kleisli (`Dir.setModificationTime` t)

-- | Get the size of a file in bytes.
--
-- >>> writeFile "/tmp/filepather_test_gs_938271" "hello"
-- >>> let Kleisli f = getFileSize in f "/tmp/filepather_test_gs_938271" >>= \s -> Dir.removeFile "/tmp/filepather_test_gs_938271" >> pure s
-- 5
getFileSize :: FilePatherIOA Integer
getFileSize = Kleisli Dir.getFileSize