|
- {-# LANGUAGE TemplateHaskell #-}
- {-# LANGUAGE DeriveGeneric #-}
- {-# LANGUAGE DeriveDataTypeable #-}
- {-# LANGUAGE EmptyDataDecls #-}
-
- -- | A normalizing well-typed path type.
-
- module Path
- (-- * Types
- Path
- ,Abs
- ,Rel
- ,File
- ,Dir
- -- * Parsing
- ,parseAbsDir
- ,parseRelDir
- ,parseAbsFile
- ,parseRelFile
- ,PathParseException
- -- * Constructors
- ,mkAbsDir
- ,mkRelDir
- ,mkAbsFile
- ,mkRelFile
- -- * Conversion
- ,toFilePath
- )
- where
-
- import Control.Exception (Exception)
- import Control.Monad.Catch (MonadThrow(..))
- import Data.Data
- import Data.List
- import Language.Haskell.TH
- import Path.Internal
- import qualified System.FilePath as FilePath
-
- --------------------------------------------------------------------------------
- -- Types
-
- -- | An absolute path.
- data Abs
-
- -- | A relative path; one without a root.
- data Rel
-
- -- | A file path.
- data File
-
- -- | A directory path.
- data Dir
-
- -- | Exception when parsing a location.
- data PathParseException
- = InvalidAbsDir FilePath
- | InvalidRelDir FilePath
- | InvalidAbsFile FilePath
- | InvalidRelFile FilePath
- deriving (Show,Typeable)
- instance Exception PathParseException
-
- --------------------------------------------------------------------------------
- -- Parsers
-
- -- | Get a location for an absolute directory. Produces a normalized
- -- path which always ends in a path separator.
- --
- -- Throws: 'PathParseException'
- --
- parseAbsDir :: MonadThrow m
- => FilePath -> m (Path Abs Dir)
- parseAbsDir filepath =
- if FilePath.isAbsolute filepath &&
- not (null (normalizeDir filepath)) &&
- not (isPrefixOf "~/" filepath)
- then return (Path (normalizeDir filepath))
- else throwM (InvalidAbsDir filepath)
-
- -- | Get a location for a relative directory. Produces a normalized
- -- path which always ends in a path separator.
- --
- -- Throws: 'PathParseException'
- --
- parseRelDir :: MonadThrow m
- => FilePath -> m (Path Rel Dir)
- parseRelDir filepath =
- if not (FilePath.isAbsolute filepath) &&
- not (null filepath) &&
- not (isPrefixOf "~/" filepath) &&
- not (null (normalizeDir filepath))
- then return (Path (normalizeDir filepath))
- else throwM (InvalidRelDir filepath)
-
- -- | Get a location for an absolute file. Produces a normalized
- -- path which always ends in a path separator.
- --
- -- Throws: 'PathParseException'
- --
- parseAbsFile :: MonadThrow m
- => FilePath -> m (Path Abs File)
- parseAbsFile filepath =
- if FilePath.isAbsolute filepath &&
- not (FilePath.hasTrailingPathSeparator filepath) &&
- not (isPrefixOf "~/" filepath) &&
- not (null (normalizeFile filepath))
- then return (Path (normalizeFile filepath))
- else throwM (InvalidAbsFile filepath)
-
- -- | Get a location for a relative file. Produces a normalized
- -- path which always ends in a path separator.
- --
- -- Throws: 'PathParseException'
- --
- parseRelFile :: MonadThrow m
- => FilePath -> m (Path Rel File)
- parseRelFile filepath =
- if not (FilePath.isAbsolute filepath || FilePath.hasTrailingPathSeparator filepath) &&
- not (null filepath) &&
- not (isPrefixOf "~/" filepath) &&
- not (null (normalizeFile filepath))
- then return (Path (normalizeFile filepath))
- else throwM (InvalidRelFile filepath)
-
- --------------------------------------------------------------------------------
- -- Constructors
-
- -- | Make a 'Path Abs Dir'.
- --
- -- Remember: due to the nature of absolute paths this (e.g. @\/home\/foo@)
- -- may compile on your platform, but it may not compile on another
- -- platform (Windows).
- mkAbsDir :: FilePath -> Q Exp
- mkAbsDir s =
- case parseAbsDir s of
- Left err -> error (show err)
- Right (Path str) ->
- [|Path $(return (LitE (StringL str))) :: Path Abs Dir|]
-
- -- | Make a 'Path Rel Dir'.
- mkRelDir :: FilePath -> Q Exp
- mkRelDir s =
- case parseRelDir s of
- Left err -> error (show err)
- Right (Path str) ->
- [|Path $(return (LitE (StringL str))) :: Path Rel Dir|]
-
- -- | Make a 'Path Abs File'.
- --
- -- Remember: due to the nature of absolute paths this (e.g. @\/home\/foo@)
- -- may compile on your platform, but it may not compile on another
- -- platform (Windows).
- mkAbsFile :: FilePath -> Q Exp
- mkAbsFile s =
- case parseAbsFile s of
- Left err -> error (show err)
- Right (Path str) ->
- [|Path $(return (LitE (StringL str))) :: Path Abs File|]
-
- -- | Make a 'Path Rel File'.
- mkRelFile :: FilePath -> Q Exp
- mkRelFile s =
- case parseRelFile s of
- Left err -> error (show err)
- Right (Path str) ->
- [|Path $(return (LitE (StringL str))) :: Path Rel File|]
-
- --------------------------------------------------------------------------------
- -- Conversion
-
- -- | Convert to a 'FilePath' type.
- toFilePath :: Path b t -> FilePath
- toFilePath (Path l) = l
-
- --------------------------------------------------------------------------------
- -- Internal functions
-
- -- | Internal use for normalizing a directory.
- normalizeDir :: FilePath -> FilePath
- normalizeDir =
- clean . FilePath.addTrailingPathSeparator . FilePath.normalise
- where clean "./" = ""
- clean ('/':'/':xs) = clean ('/':xs)
- clean x = x
-
- -- | Internal use for normalizing a fileectory.
- normalizeFile :: FilePath -> FilePath
- normalizeFile =
- clean . FilePath.normalise
- where clean "./" = ""
- clean ('/':'/':xs) = clean ('/':xs)
- clean x = x
|