packages feed

path-tagged-0.1.0.0: src/Path/Tagged/IO.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

module Path.Tagged.IO (
  -- * Actions on directories
  createDir,
  createDirIfMissing,
  ensureDir,
  removeDir,
  removeDirRecur,
  removePathForcibly,
  renameDir,
  renamePath,
  listDir,
  listDirRel,
  listDirRecur,
  listDirRecurRel,
  copyDirRecur,
  copyDirRecur',

  -- * Walking directory trees
  WalkAction (..),
  walkDir,
  walkDirRel,
  walkDirAccum,
  walkDirAccumRel,

  -- * Current working directory
  getCurrentDir,
  setCurrentDir,
  withCurrentDir,

  -- * Pre-defined directories
  PredefinedDir (..),
  WithPredefined,
  Cwd,
  Home,
  AppUserData,
  UserDocs,
  TempDir,
  getHomeDir,
  getAppUserDataDir,
  getUserDocsDir,
  getTempDir,
  XdgDirectory (..),
  XdgData,
  XdgConfig,
  XdgCache,
  WithXdg,
  KnownXdgDirectory (),
  getXdgBaseDir,
  getXdgDir,
  getXdgDataDirs,
  getXdgConfigDirs,

  -- * Path transformation
  AnyPathTo (..),
  RelPathTo,
  resolveFile,
  resolveFile',
  resolveDir,
  resolveDir',

  -- * Actions on Files
  removeFile,
  renameFile,
  copyFile,
  getFileSize,
  findExecutable,
  findFile,
  findFiles,
  findFilesWith,

  -- * Symbolic links
  createFileLink,
  createDirLink,
  removeDirLink,
  getSymlinkTarget,
  isSymlink,

  -- * Temporary files and directories
  withTempFile,
  withTempDir,
  withSystemTempFile,
  withSystemTempDir,
  openTempFile,
  openBinaryTempFile,
  createTempDir,

  -- * Existence tests
  doesPathExist,
  doesFileExist,
  doesDirExist,
  isLocationOccupied,
  forgivingAbsence,
  ignoringAbsence,

  -- * Permissions
  Permissions (..),
  emptyPermissions,
  getPermissions,
  setPermissions,
  copyPermissions,

  -- * Timestamps
  getAccessTime,
  setAccessTime,
  getModificationTime,
  setModificationTime,
) where

import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
import Control.Monad.IO.Class (MonadIO)
import Data.Coerce (coerce)
import Data.Functor ((<&>))
import Data.Kind (Constraint, Type)
import Data.Time (UTCTime)
import GHC.Exts (Proxy#, proxy#)
import GHC.Generics (Generic)
import Path.IO (Permissions (..), XdgDirectory (..), emptyPermissions, forgivingAbsence, ignoringAbsence)
import qualified Path.IO as P
import Path.Tagged
import System.IO (Handle)

createDir :: (MonadIO m) => PathTo e b Dir -> m ()
createDir = P.createDir . coerce

createDirIfMissing :: (MonadIO m) => Bool -> PathTo e b Dir -> m ()
createDirIfMissing p = P.createDirIfMissing p . coerce

ensureDir :: (MonadIO m) => PathTo e b Dir -> m ()
ensureDir = P.ensureDir . coerce

removeDir :: (MonadIO m) => PathTo entity b Dir -> m ()
removeDir = P.removeDir . untagPath

removeDirRecur :: (MonadIO m) => PathTo entity b Dir -> m ()
removeDirRecur = P.removeDirRecur . untagPath

removePathForcibly :: (MonadIO m) => PathTo e b t -> m ()
removePathForcibly = P.removePathForcibly . untagPath

renameDir :: (MonadIO m) => PathTo e b Dir -> PathTo e b' Dir -> m ()
renameDir (PathTo old) (PathTo new) = P.renameDir old new

renamePath :: (MonadIO m) => PathTo e b t -> PathTo e b' t -> m ()
renamePath (PathTo old) (PathTo new) = P.renamePath old new

listDir ::
  (MonadIO m) =>
  PathTo e b Dir ->
  m ([PathTo Unknown Abs Dir], [PathTo Unknown Abs File])
listDir = fmap coerce . P.listDir . untagPath

listDirRel ::
  (MonadIO m) =>
  PathTo e b Dir ->
  m ([PathTo Unknown (RelTo e) Dir], [PathTo Unknown (RelTo e) File])
listDirRel = fmap coerce . P.listDirRel . untagPath

listDirRecur ::
  (MonadIO m) =>
  PathTo e b Dir ->
  m ([PathTo Unknown Abs Dir], [PathTo Unknown Abs File])
listDirRecur = fmap coerce . P.listDirRecur . untagPath

listDirRecurRel ::
  (MonadIO m) =>
  PathTo e b Dir ->
  m ([PathTo Unknown (RelTo e) Dir], [PathTo Unknown (RelTo e) File])
listDirRecurRel = fmap coerce . P.listDirRecurRel . untagPath

copyDirRecur ::
  (MonadIO m, MonadCatch m) =>
  PathTo e0 b0 Dir ->
  PathTo e1 b1 Dir ->
  m ()
copyDirRecur (PathTo p) (PathTo l) = P.copyDirRecur p l

copyDirRecur' ::
  (MonadIO m, MonadCatch m) =>
  PathTo e0 b0 Dir ->
  PathTo e1 b1 Dir ->
  m ()
copyDirRecur' (PathTo p) (PathTo l) = P.copyDirRecur' p l

data WalkAction b = WalkFinish | WalkExclude [PathTo Unknown b Dir]
  deriving (Show, Eq, Ord)

toUntaggedAct :: WalkAction b -> P.WalkAction (Untag b)
{-# INLINE toUntaggedAct #-}
toUntaggedAct WalkFinish = P.WalkFinish
toUntaggedAct (WalkExclude excs) = P.WalkExclude $ coerce excs

walkDir ::
  (MonadIO m) =>
  ( forall dir.
    PathTo dir Abs Dir ->
    [PathTo Unknown Abs Dir] ->
    [PathTo Unknown Abs File] ->
    m (WalkAction Abs)
  ) ->
  PathTo e b Dir ->
  m ()
walkDir f p =
  P.walkDir
    ( \dir subdir subfile -> toUntaggedAct <$> f (PathTo dir) (PathTo <$> subdir) (PathTo <$> subfile)
    )
    (untagPath p)

walkDirRel ::
  (MonadIO m) =>
  ( forall dir.
    PathTo dir (RelTo e) Dir ->
    [PathTo Unknown (RelTo dir) Dir] ->
    [PathTo Unknown (RelTo dir) File] ->
    m (WalkAction (RelTo dir))
  ) ->
  PathTo e b Dir ->
  m ()
walkDirRel f p =
  P.walkDirRel
    ( \dir subdir subfile -> toUntaggedAct <$> f (PathTo dir) (PathTo <$> subdir) (PathTo <$> subfile)
    )
    (untagPath p)

walkDirAccum ::
  (MonadIO m, Monoid o) =>
  Maybe
    ( forall dir.
      PathTo dir Abs Dir ->
      [PathTo Unknown Abs Dir] ->
      [PathTo Unknown Abs File] ->
      m (WalkAction Abs)
    ) ->
  ( forall dir.
    PathTo dir Abs Dir ->
    [PathTo Unknown Abs Dir] ->
    [PathTo Unknown Abs File] ->
    m o
  ) ->
  PathTo e b Dir ->
  m o
walkDirAccum mf g p =
  P.walkDirAccum
    ( mf <&> \f dir subdir subfile -> toUntaggedAct <$> f (PathTo dir) (coerce subdir) (coerce subfile)
    )
    (\dir subdir subfile -> g (PathTo dir) (coerce subdir) (coerce subfile))
    (untagPath p)

walkDirAccumRel ::
  (MonadIO m, Monoid o) =>
  Maybe
    ( forall dir.
      PathTo dir (RelTo e) Dir ->
      [PathTo Unknown (RelTo dir) Dir] ->
      [PathTo Unknown (RelTo dir) File] ->
      m (WalkAction (RelTo dir))
    ) ->
  ( forall dir.
    PathTo dir (RelTo e) Dir ->
    [PathTo Unknown (RelTo dir) Dir] ->
    [PathTo Unknown (RelTo dir) File] ->
    m o
  ) ->
  PathTo e b Dir ->
  m o
walkDirAccumRel mf g p =
  P.walkDirAccumRel
    ( mf <&> \f dir subdir subfile -> toUntaggedAct <$> f (PathTo dir) (coerce subdir) (coerce subfile)
    )
    (\dir subdir subfile -> g (PathTo dir) (coerce subdir) (coerce subfile))
    (untagPath p)

getCurrentDir :: forall m. (MonadIO m) => m (PathTo Cwd Abs Dir)
getCurrentDir = PathTo <$> P.getCurrentDir

setCurrentDir :: (MonadIO m) => PathTo e b Dir -> m ()
setCurrentDir = P.setCurrentDir . coerce

withCurrentDir :: (MonadIO m, MonadMask m) => PathTo e b Dir -> m a -> m a
withCurrentDir = P.withCurrentDir . coerce

type WithPredefined :: PredefinedDir -> k
type family WithPredefined p = w | w -> p where

data PredefinedDir
  = Home
  | AppUserData
  | UserDocs
  | TempDir
  | Cwd
  deriving (Show, Eq, Ord, Generic)

type Cwd = WithPredefined 'Cwd

type Home = WithPredefined 'Home

type AppUserData = WithPredefined 'AppUserData

type UserDocs = WithPredefined 'UserDocs

type TempDir = WithPredefined 'TempDir

getHomeDir :: (MonadIO m) => m (PathTo Home Abs Dir)
getHomeDir = PathTo <$> P.getHomeDir

getAppUserDataDir :: (MonadIO m) => String -> m (PathTo AppUserData Abs Dir)
getAppUserDataDir = fmap PathTo . P.getAppUserDataDir

getUserDocsDir :: (MonadIO m) => m (PathTo UserDocs Abs Dir)
getUserDocsDir = PathTo <$> P.getUserDocsDir

getTempDir :: (MonadIO m) => m (PathTo TempDir Abs Dir)
getTempDir = PathTo <$> P.getTempDir

type WithXdg :: forall {k}. XdgDirectory -> k
type family WithXdg xdg = p | p -> xdg where

type XdgData = WithXdg 'XdgData

type XdgConfig = WithXdg 'XdgConfig

type XdgCache = WithXdg 'XdgCache

type KnownXdgDirectory :: XdgDirectory -> Constraint
class KnownXdgDirectory xdg where
  xdgDirectory# :: Proxy# xdg -> XdgDirectory

instance KnownXdgDirectory 'XdgData where
  xdgDirectory# _ = XdgData

instance KnownXdgDirectory 'XdgConfig where
  xdgDirectory# _ = XdgConfig

instance KnownXdgDirectory 'XdgCache where
  xdgDirectory# _ = XdgCache

getXdgBaseDir ::
  forall xdg m.
  (KnownXdgDirectory xdg, MonadIO m) =>
  m (PathTo xdg Abs Dir)
getXdgBaseDir = PathTo <$> P.getXdgDir (xdgDirectory# (proxy# :: Proxy# xdg)) Nothing

getXdgDir ::
  forall xdg e m.
  (KnownXdgDirectory xdg, MonadIO m) =>
  PathTo e (RelTo (WithXdg xdg)) Dir ->
  m (PathTo e Abs Dir)
getXdgDir (PathTo p) =
  PathTo <$> P.getXdgDir (xdgDirectory# (proxy# :: Proxy# xdg)) (Just p)

getXdgDataDirs :: (MonadIO m) => m [PathTo XdgData Abs Dir]
getXdgDataDirs = coerce <$> P.getXdgDirList P.XdgDataDirs

getXdgConfigDirs :: (MonadIO m) => m [PathTo XdgConfig Abs Dir]
getXdgConfigDirs = coerce <$> P.getXdgDirList P.XdgConfigDirs

type RelPathTo :: forall {k}. k -> Type -> Type
type RelPathTo (e :: k) path = RelPathTo' k e path

class AnyPathTo path where
  type PathTag path :: Type
  type AbsPath path :: Type
  type RelPathTo' k (e :: k) path :: Type
  canonicalizePath :: (MonadIO m) => path -> m (AbsPath path)
  makeAbsolute :: (MonadIO m) => path -> m (AbsPath path)
  makeRelative :: (MonadThrow m) => PathTo (e :: PathTag path) Abs Dir -> path -> m (RelPathTo e path)
  makeRelativeToCurrentDir :: (MonadIO m) => path -> m (RelPathTo' (PathTag path) Cwd path)

instance AnyPathTo (SomeBase (e :: k) b Dir) where
  type PathTag (SomeBase (e :: k) b Dir) = k
  type AbsPath (SomeBase e b Dir) = PathTo e Abs Dir
  type RelPathTo' k e' (SomeBase e b Dir) = PathTo e (RelTo e') Dir
  canonicalizePath = \case
    IsAbs fp -> canonicalizePath fp
    IsRel fp -> canonicalizePath fp
  makeAbsolute = \case
    IsAbs fp -> makeAbsolute fp
    IsRel fp -> makeAbsolute fp
  makeRelative b = \case
    IsAbs fp -> makeRelative b fp
    IsRel fp -> makeRelative b fp
  makeRelativeToCurrentDir = \case
    IsAbs fp -> makeRelativeToCurrentDir fp
    IsRel fp -> makeRelativeToCurrentDir fp

instance AnyPathTo (SomeBase (e :: k) b File) where
  type PathTag (SomeBase (e :: k) b File) = k
  type AbsPath (SomeBase e b File) = PathTo e Abs File
  type RelPathTo' k e' (SomeBase e b File) = PathTo e (RelTo e') File
  canonicalizePath = \case
    IsAbs fp -> canonicalizePath fp
    IsRel fp -> canonicalizePath fp
  makeAbsolute = \case
    IsAbs fp -> makeAbsolute fp
    IsRel fp -> makeAbsolute fp
  makeRelative b = \case
    IsAbs fp -> makeRelative b fp
    IsRel fp -> makeRelative b fp
  makeRelativeToCurrentDir = \case
    IsAbs fp -> makeRelativeToCurrentDir fp
    IsRel fp -> makeRelativeToCurrentDir fp

instance AnyPathTo (PathTo (e :: k) b File) where
  type PathTag (PathTo (e :: k) b File) = k
  type AbsPath (PathTo e b File) = PathTo e Abs File
  type RelPathTo' k e' (PathTo e b File) = PathTo e (RelTo e') File
  canonicalizePath = fmap PathTo . P.canonicalizePath . untagPath
  makeAbsolute = fmap PathTo . P.makeAbsolute . untagPath
  makeRelative (PathTo p) = fmap PathTo . P.makeRelative p . untagPath
  makeRelativeToCurrentDir = fmap PathTo . P.makeRelativeToCurrentDir . untagPath

instance AnyPathTo (PathTo (e :: k) b Dir) where
  type PathTag (PathTo (e :: k) b Dir) = k
  type AbsPath (PathTo e b Dir) = PathTo e Abs Dir
  type RelPathTo' k e' (PathTo e b Dir) = PathTo e (RelTo e') Dir
  canonicalizePath = fmap PathTo . P.canonicalizePath . untagPath
  makeAbsolute = fmap PathTo . P.makeAbsolute . untagPath
  makeRelative (PathTo p) = fmap PathTo . P.makeRelative p . untagPath
  makeRelativeToCurrentDir = fmap PathTo . P.makeRelativeToCurrentDir . untagPath

resolveFile ::
  forall e e0 m.
  (MonadIO m) =>
  PathTo e0 Abs Dir ->
  FilePath ->
  m (PathTo e Abs File)
resolveFile = fmap (fmap PathTo) . P.resolveFile . untagPath

resolveFile' :: forall e m. (MonadIO m) => FilePath -> m (PathTo e Abs File)
resolveFile' = fmap PathTo . P.resolveFile'

resolveDir ::
  forall e e0 m.
  (MonadIO m) =>
  PathTo e0 Abs Dir ->
  FilePath ->
  m (PathTo e Abs Dir)
resolveDir = fmap (fmap PathTo) . P.resolveDir . untagPath

resolveDir' :: forall e m. (MonadIO m) => FilePath -> m (PathTo e Abs Dir)
resolveDir' = fmap PathTo . P.resolveDir'

removeFile :: (MonadIO m) => PathTo e b File -> m ()
removeFile = P.removeFile . untagPath

renameFile :: (MonadIO m) => PathTo e b0 File -> PathTo e b1 File -> m ()
renameFile (PathTo p) = P.renameFile p . untagPath

copyFile :: (MonadIO m) => PathTo e0 b0 File -> PathTo e1 b1 File -> m ()
copyFile (PathTo p) = P.copyFile p . untagPath

getFileSize :: (MonadIO m) => PathTo e b File -> m Integer
getFileSize = P.getFileSize . untagPath

findExecutable :: (MonadIO m) => PathTo e (RelTo b) File -> m (Maybe (PathTo e Abs File))
findExecutable = fmap coerce . P.findExecutable . untagPath

findFile ::
  (MonadIO m) =>
  [PathTo dir b Dir] ->
  PathTo e (RelTo dir) File ->
  m (Maybe (PathTo e Abs File))
findFile dirs = fmap coerce . P.findFile (coerce dirs) . untagPath

findFiles ::
  (MonadIO m) =>
  [PathTo dir b Dir] ->
  PathTo e (RelTo dir) File ->
  m [PathTo e Abs File]
findFiles dirs = fmap coerce . P.findFiles (coerce dirs) . untagPath

findFilesWith ::
  (MonadIO m) =>
  (PathTo e Abs File -> m Bool) ->
  [PathTo dir b Dir] ->
  PathTo e (RelTo dir) File ->
  m [PathTo e Abs File]
findFilesWith p dirs =
  fmap coerce . P.findFilesWith (p . PathTo) (coerce dirs) . untagPath

createFileLink :: (MonadIO m) => PathTo e b0 File -> PathTo e b1 File -> m ()
createFileLink (PathTo p) = P.createFileLink p . untagPath

createDirLink :: (MonadIO m) => PathTo e b0 Dir -> PathTo e b1 Dir -> m ()
createDirLink (PathTo p) = P.createDirLink p . untagPath

removeDirLink :: (MonadIO m) => PathTo e b0 Dir -> m ()
removeDirLink = P.removeDirLink . untagPath

getSymlinkTarget :: (MonadIO m) => PathTo e b t -> m FilePath
getSymlinkTarget = P.getSymlinkTarget . untagPath

isSymlink :: (MonadIO m) => PathTo e b t -> m Bool
isSymlink = P.isSymlink . untagPath

withTempFile ::
  forall e e0 b m a.
  (MonadIO m, MonadMask m) =>
  PathTo e0 b Dir ->
  String ->
  (PathTo e Abs File -> Handle -> m a) ->
  m a
withTempFile (PathTo d) name act = P.withTempFile d name $ act . PathTo

withTempDir ::
  (MonadIO m, MonadMask m) =>
  PathTo e b Dir ->
  String ->
  (PathTo TempDir Abs Dir -> m a) ->
  m a
withTempDir (PathTo d) name act = P.withTempDir d name $ act . PathTo

withSystemTempFile ::
  forall e m a.
  (MonadIO m, MonadMask m) =>
  String ->
  (PathTo e Abs File -> Handle -> m a) ->
  m a
withSystemTempFile name act = P.withSystemTempFile name $ act . PathTo

withSystemTempDir ::
  (MonadIO m, MonadMask m) =>
  String ->
  (PathTo TempDir Abs Dir -> m a) ->
  m a
withSystemTempDir name act = P.withSystemTempDir name $ act . PathTo

openTempFile ::
  forall e e0 b m.
  (MonadIO m) =>
  PathTo e0 b Dir ->
  String ->
  m (PathTo e Abs File, Handle)
openTempFile (PathTo p) = fmap coerce . P.openTempFile p

openBinaryTempFile ::
  forall e e0 b m.
  (MonadIO m) =>
  PathTo e0 b Dir ->
  String ->
  m (PathTo e Abs File, Handle)
openBinaryTempFile (PathTo p) = fmap coerce . P.openBinaryTempFile p

createTempDir :: (MonadIO m) => PathTo e b Dir -> String -> m (PathTo TempDir Abs Dir)
createTempDir (PathTo p) = fmap PathTo . P.createTempDir p

doesPathExist :: (MonadIO m) => PathTo e b t -> m Bool
doesPathExist = P.doesPathExist . untagPath

doesFileExist :: (MonadIO m) => PathTo e b File -> m Bool
doesFileExist = P.doesFileExist . untagPath

doesDirExist :: (MonadIO m) => PathTo e b Dir -> m Bool
doesDirExist = P.doesDirExist . untagPath

isLocationOccupied :: (MonadIO m) => PathTo e b t -> m Bool
isLocationOccupied = P.isLocationOccupied . untagPath

getPermissions :: (MonadIO m) => PathTo e b t -> m Permissions
getPermissions = P.getPermissions . untagPath

setPermissions :: (MonadIO m) => PathTo e b t -> Permissions -> m ()
setPermissions = P.setPermissions . untagPath

copyPermissions :: (MonadIO m) => PathTo e0 b0 t0 -> PathTo e1 b1 t1 -> m ()
copyPermissions (PathTo p) = P.copyPermissions p . untagPath

getAccessTime :: (MonadIO m) => PathTo entity pk t -> m UTCTime
getAccessTime = P.getAccessTime . untagPath

setAccessTime :: (MonadIO m) => PathTo entity pk t -> UTCTime -> m ()
setAccessTime = P.setAccessTime . untagPath

getModificationTime :: (MonadIO m) => PathTo entity pk t -> m UTCTime
getModificationTime = P.getModificationTime . untagPath

setModificationTime :: (MonadIO m) => PathTo entity pk t -> UTCTime -> m ()
setModificationTime = P.setModificationTime . untagPath