Safe Haskell | Safe |
---|---|
Language | Haskell98 |
This module provides type-safe access to filepath manipulations.
Normally you would import Path
(which will use the
default implementation for the host platform) instead of this.
However, importing this explicitly allows for manipulation of
non-native paths.
- data Path ar fd
- data Abs
- data Rel
- data File
- data Dir
- type AbsFile = Path Abs File
- type RelFile = Path Rel File
- type AbsDir = Path Abs Dir
- type RelDir = Path Rel Dir
- type AbsPath fd = Path Abs fd
- type RelPath fd = Path Rel fd
- type FilePath ar = Path ar File
- type DirPath ar = Path ar Dir
- class Private ar => AbsRelClass ar where
- class Private fd => FileDirClass fd where
- getPathString :: (AbsRelClass ar, FileDirClass fd) => Path ar fd -> String
- rootDir :: AbsDir
- currentDir :: RelDir
- asPath :: (AbsRelClass ar, FileDirClass fd) => String -> Path ar fd
- asRelFile :: String -> RelFile
- asRelDir :: String -> RelDir
- asAbsFile :: String -> AbsFile
- asAbsDir :: String -> AbsDir
- asRelPath :: FileDirClass fd => String -> RelPath fd
- asAbsPath :: FileDirClass fd => String -> AbsPath fd
- asFilePath :: AbsRelClass ar => String -> FilePath ar
- asDirPath :: AbsRelClass ar => String -> DirPath ar
- mkPathAbsOrRel :: FileDirClass fd => String -> Either (AbsPath fd) (RelPath fd)
- mkPathFileOrDir :: AbsRelClass ar => String -> IO (Maybe (Either (FilePath ar) (DirPath ar)))
- mkAbsPath :: FileDirClass fd => AbsDir -> String -> AbsPath fd
- mkAbsPathFromCwd :: FileDirClass fd => String -> IO (AbsPath fd)
- (</>) :: DirPath ar -> RelPath fd -> Path ar fd
- (<.>) :: FilePath ar -> String -> FilePath ar
- addExtension :: FilePath ar -> String -> FilePath ar
- combine :: DirPath ar -> RelPath fd -> Path ar fd
- dropExtension :: FilePath ar -> FilePath ar
- dropExtensions :: FilePath ar -> FilePath ar
- dropFileName :: FilePath ar -> DirPath ar
- replaceExtension :: FilePath ar -> String -> FilePath ar
- replaceBaseName :: FilePath ar -> String -> FilePath ar
- replaceDirectory :: FilePath ar1 -> DirPath ar2 -> FilePath ar2
- replaceFileName :: FilePath ar -> String -> FilePath ar
- splitExtension :: FilePath ar -> (FilePath ar, String)
- splitExtensions :: FilePath ar -> (FilePath ar, String)
- splitFileName :: FilePath ar -> (DirPath ar, RelFile)
- takeBaseName :: FilePath ar -> RelFile
- takeDirectory :: FilePath ar -> DirPath ar
- takeExtension :: FilePath ar -> String
- takeExtensions :: FilePath ar -> String
- takeFileName :: FilePath ar -> RelFile
- equalFilePath :: String -> String -> Bool
- joinPath :: FileDirClass fd => [String] -> RelPath fd
- normalise :: Path ar fd -> Path ar fd
- splitPath :: (AbsRelClass ar, FileDirClass fd) => Path ar fd -> (Bool, [RelDir], Maybe RelFile)
- makeRelative :: FileDirClass fd => AbsDir -> AbsPath fd -> RelPath fd
- makeAbsolute :: AbsDir -> RelPath fd -> AbsPath fd
- makeAbsoluteFromCwd :: RelPath fd -> IO (AbsPath fd)
- genericMakeAbsolute :: AbsRelClass ar => AbsDir -> Path ar fd -> AbsPath fd
- genericMakeAbsoluteFromCwd :: AbsRelClass ar => Path ar fd -> IO (AbsPath fd)
- pathMap :: FileDirClass fd => (String -> String) -> Path ar fd -> Path ar fd
- isAbsolute :: AbsRelClass ar => Path ar fd -> Bool
- isAbsoluteString :: String -> Bool
- isRelative :: AbsRelClass ar => Path ar fd -> Bool
- isRelativeString :: String -> Bool
- hasAnExtension :: FilePath ar -> Bool
- hasExtension :: String -> FilePath ar -> Bool
- addTrailingPathSeparator :: String -> String
- dropTrailingPathSeparator :: String -> String
- extSeparator :: Char
- hasTrailingPathSeparator :: String -> Bool
- pathSeparator :: Char
- pathSeparators :: [Char]
- searchPathSeparator :: Char
- isExtSeparator :: Char -> Bool
- isPathSeparator :: Char -> Bool
- isSearchPathSeparator :: Char -> Bool
- genericAddExtension :: FileDirClass fd => Path ar fd -> String -> Path ar fd
- genericDropExtension :: FileDirClass fd => Path ar fd -> Path ar fd
- genericDropExtensions :: FileDirClass fd => Path ar fd -> Path ar fd
- genericSplitExtension :: FileDirClass fd => Path ar fd -> (Path ar fd, String)
- genericSplitExtensions :: FileDirClass fd => Path ar fd -> (Path ar fd, String)
- genericTakeExtension :: FileDirClass fd => Path ar fd -> String
- genericTakeExtensions :: FileDirClass fd => Path ar fd -> String
The main filepath (& dirpath) abstract type
This is the main filepath abstract datatype
(AbsRelClass ar, FileDirClass fd) => Eq (Path ar fd) Source | |
(AbsRelClass ar, FileDirClass fd) => Ord (Path ar fd) Source | |
(AbsRelClass ar, FileDirClass fd) => Read (Path ar fd) Source | |
(AbsRelClass ar, FileDirClass fd) => Show (Path ar fd) Source | |
(AbsRelClass ar, FileDirClass fd) => IsString (Path ar fd) Source | Allow use of OverloadedStrings if desired |
(AbsRelClass ar, FileDirClass fd) => Arbitrary (Path ar fd) Source | |
(AbsRelClass ar, FileDirClass fd) => NFData (Path ar fd) Source |
Phantom Types
Type Synonyms
Classes
class Private ar => AbsRelClass ar where Source
This class allows selective behaviour for absolute and relative paths and is mostly for internal use.
switchAbsRel :: f Abs -> f Rel -> f ar Source
See https://wiki.haskell.org/Closed_world_instances for the used technique.
absRel :: (AbsPath fd -> a) -> (RelPath fd -> a) -> Path ar fd -> a Source
Will become a top-level function in future
class Private fd => FileDirClass fd where Source
This class allows selective behaviour for file and directory paths and is mostly for internal use.
Path to String conversion
getPathString :: (AbsRelClass ar, FileDirClass fd) => Path ar fd -> String Source
Constants
Unchecked Construction Functions
asPath :: (AbsRelClass ar, FileDirClass fd) => String -> Path ar fd Source
Use a String
as a Path
whose type is determined
by its context.
> Posix.asPath "/tmp" == Posix.asAbsDir "/tmp" > Posix.asPath "file.txt" == Posix.asRelFile "file.txt" > Posix.isAbsolute (Posix.asAbsDir "/tmp") > Posix.isRelative (Posix.asRelDir "/tmp") > Posix.getPathString (Posix.asPath "/tmp" :: Posix.AbsDir) == "/tmp" > Posix.getPathString (Posix.asPath "/tmp" :: Posix.RelDir) == "tmp" > Windows.getPathString (Windows.asPath "\\tmp" :: Windows.AbsDir) == "\\tmp" > Windows.getPathString (Windows.asPath "a:\\tmp" :: Windows.AbsDir) == "a:\\tmp" > Windows.getPathString (Windows.asPath "tmp" :: Windows.RelDir) == "tmp"
asRelDir :: String -> RelDir Source
Use a String
as a RelDir
. No checking is done.
> Posix.getPathString (Posix.asRelDir ".") == "." > Posix.getPathString (Posix.asRelDir "file.txt") == "file.txt" > Posix.getPathString (Posix.asRelDir "/file.txt") == "file.txt" > Posix.getPathString (Posix.asRelDir "tmp") == "tmp" > Posix.getPathString (Posix.asRelDir "/tmp") == "tmp"
asRelPath :: FileDirClass fd => String -> RelPath fd Source
Use a String
as a 'RelPath fd'. No checking is done.
asAbsPath :: FileDirClass fd => String -> AbsPath fd Source
Use a String
as an 'AbsPath fd'. No checking is done.
asFilePath :: AbsRelClass ar => String -> FilePath ar Source
Use a String
as a 'FilePath ar'. No checking is done.
asDirPath :: AbsRelClass ar => String -> DirPath ar Source
Use a String
as a 'DirPath ar'. No checking is done.
Checked Construction Functions
mkPathAbsOrRel :: FileDirClass fd => String -> Either (AbsPath fd) (RelPath fd) Source
Examines the supplied string and constructs an absolute or relative path as appropriate.
> Posix.mkPathAbsOrRel "/tmp" == Left (Posix.asAbsDir "/tmp") > Posix.mkPathAbsOrRel "tmp" == Right (Posix.asRelDir "tmp") > Windows.mkPathAbsOrRel "\\tmp" == Left (Windows.asAbsDir "\\tmp") > Windows.mkPathAbsOrRel "d:\\tmp" == Left (Windows.asAbsDir "d:\\tmp") > Windows.mkPathAbsOrRel "tmp" == Right (Windows.asRelDir "tmp")
mkPathFileOrDir :: AbsRelClass ar => String -> IO (Maybe (Either (FilePath ar) (DirPath ar))) Source
mkAbsPathFromCwd :: FileDirClass fd => String -> IO (AbsPath fd) Source
Basic Manipulation Functions
(</>) :: DirPath ar -> RelPath fd -> Path ar fd Source
Infix variant of combine
.
> Posix.getPathString (Posix.asAbsDir "/tmp" </> Posix.asRelFile "file.txt") == "/tmp/file.txt" > Posix.getPathString (Posix.asAbsDir "/tmp" </> Posix.asRelDir "dir" </> Posix.asRelFile "file.txt") == "/tmp/dir/file.txt" > Posix.getPathString (Posix.asRelDir "dir" </> Posix.asRelFile "file.txt") == "dir/file.txt" > Windows.getPathString (Windows.asAbsDir "\\tmp" Windows.</> Windows.asRelFile "file.txt") == "\\tmp\\file.txt" > Windows.getPathString (Windows.asAbsDir "c:\\tmp" Windows.</> Windows.asRelFile "file.txt") == "c:\\tmp\\file.txt" > Windows.getPathString (Windows.asAbsDir "c:" Windows.</> Windows.asRelDir "tmp" Windows.</> Windows.asRelFile "file.txt") == "c:\\tmp\\file.txt" > Windows.getPathString (Windows.asRelDir "dir" Windows.</> Windows.asRelFile "file.txt") == "dir\\file.txt"
(<.>) :: FilePath ar -> String -> FilePath ar Source
Infix variant of addExtension
.
We only allow files (and not directories) to have extensions added
by this function. This is because it's the vastly common case and
an attempt to add one to a directory will - more often than not -
represent an error.
We don't however want to prevent the corresponding operation on
directories, and so we provide a function that is more flexible:
genericAddExtension
.
addExtension :: FilePath ar -> String -> FilePath ar Source
Add an extension, even if there is already one there.
E.g. addExtension "foo.txt" "bat" -> "foo.txt.bat"
.
> Posix.addExtension (Posix.asRelFile "file.txt") "bib" == "file.txt.bib" > Posix.addExtension (Posix.asRelFile "file.") ".bib" == "file..bib" > Posix.addExtension (Posix.asRelFile "file") ".bib" == "file.bib" > Posix.addExtension (Posix.asRelFile "") "bib" == ".bib" > Posix.addExtension (Posix.asRelFile "") ".bib" == ".bib" > Posix.takeFileName (Posix.addExtension (Posix.asRelFile "") "ext") == ".ext"
combine :: DirPath ar -> RelPath fd -> Path ar fd Source
Join an (absolute or relative) directory path with a relative (file or directory) path to form a new path.
dropExtension :: FilePath ar -> FilePath ar Source
Remove last extension, and the "." preceding it.
> Posix.dropExtension x == fst (Posix.splitExtension x)
dropExtensions :: FilePath ar -> FilePath ar Source
Drop all extensions
> not $ Posix.hasAnExtension (Posix.dropExtensions x)
dropFileName :: FilePath ar -> DirPath ar Source
Synonym for takeDirectory
replaceExtension :: FilePath ar -> String -> FilePath ar Source
Set the extension of a file, overwriting one if already present.
> Posix.replaceExtension (Posix.asRelFile "file.txt") ".bob" == "file.bob" > Posix.replaceExtension (Posix.asRelFile "file.txt") "bob" == "file.bob" > Posix.replaceExtension (Posix.asRelFile "file") ".bob" == "file.bob" > Posix.replaceExtension (Posix.asRelFile "file.txt") "" == "file" > Posix.replaceExtension (Posix.asRelFile "file.fred.bob") "txt" == "file.fred.txt"
replaceBaseName :: FilePath ar -> String -> FilePath ar Source
replaceDirectory :: FilePath ar1 -> DirPath ar2 -> FilePath ar2 Source
replaceFileName :: FilePath ar -> String -> FilePath ar Source
splitExtension :: FilePath ar -> (FilePath ar, String) Source
Split on the extension. addExtension
is the inverse.
> uncurry (<.>) (Posix.splitExtension x) == x > uncurry Posix.addExtension (Posix.splitExtension x) == x > Posix.splitExtension (Posix.asRelFile "file.txt") == ("file",".txt") > Posix.splitExtension (Posix.asRelFile "file") == ("file","") > Posix.splitExtension (Posix.asRelFile "file/file.txt") == ("file/file",".txt") > Posix.splitExtension (Posix.asRelFile "file.txt/boris") == ("file.txt/boris","") > Posix.splitExtension (Posix.asRelFile "file.txt/boris.ext") == ("file.txt/boris",".ext") > Posix.splitExtension (Posix.asRelFile "file/path.txt.bob.fred") == ("file/path.txt.bob",".fred")
splitExtensions :: FilePath ar -> (FilePath ar, String) Source
Split on all extensions
> Posix.splitExtensions (Posix.asRelFile "file.tar.gz") == ("file",".tar.gz")
splitFileName :: FilePath ar -> (DirPath ar, RelFile) Source
takeBaseName :: FilePath ar -> RelFile Source
Get the basename of a file
> Posix.takeBaseName (Posix.asAbsFile "/tmp/somedir/myfile.txt") == "myfile" > Posix.takeBaseName (Posix.asRelFile "./myfile.txt") == "myfile" > Posix.takeBaseName (Posix.asRelFile "myfile.txt") == "myfile"
takeDirectory :: FilePath ar -> DirPath ar Source
takeExtension :: FilePath ar -> String Source
Get the extension of a file, returns ""
for no extension, .ext
otherwise.
> Posix.takeExtension x == snd (Posix.splitExtension x) > Posix.takeExtension (Posix.addExtension x "ext") == ".ext" > Posix.takeExtension (Posix.replaceExtension x "ext") == ".ext"
takeExtensions :: FilePath ar -> String Source
Get all extensions
> Posix.takeExtensions (Posix.asRelFile "file.tar.gz") == ".tar.gz"
takeFileName :: FilePath ar -> RelFile Source
Get the filename component of a file path (ie stripping all parent dirs)
> Posix.takeFileName (Posix.asAbsFile "/tmp/somedir/myfile.txt") == "myfile.txt" > Posix.takeFileName (Posix.asRelFile "./myfile.txt") == "myfile.txt" > Posix.takeFileName (Posix.asRelFile "myfile.txt") == "myfile.txt"
Auxillary Manipulation Functions
equalFilePath :: String -> String -> Bool Source
Check whether two strings are equal as file paths.
> Posix.equalFilePath "/tmp/" "/tmp" > not $ Posix.equalFilePath "/tmp" "tmp" > Windows.equalFilePath "file" "File" > not $ Windows.equalFilePath "file" "dir"
joinPath :: FileDirClass fd => [String] -> RelPath fd Source
Constructs a RelPath
from a list of components.
It is an unchecked error if the path components contain path separators.
It is an unchecked error if a RelFile
path is empty.
> Posix.joinPath ["tmp","someDir","dir"] == Posix.asRelDir "tmp/someDir/dir" > Posix.joinPath ["tmp","someDir","file.txt"] == Posix.asRelFile "tmp/someDir/file.txt"
normalise :: Path ar fd -> Path ar fd Source
Currently just transforms:
> Posix.normalise "/tmp/fred/./jim/./file" == Posix.asAbsFile "/tmp/fred/jim/file"
splitPath :: (AbsRelClass ar, FileDirClass fd) => Path ar fd -> (Bool, [RelDir], Maybe RelFile) Source
Deconstructs a path into its components.
> Posix.splitPath (Posix.asAbsDir "/tmp/someDir/mydir.dir") == (True, ["tmp","someDir","mydir.dir"], Nothing) > Posix.splitPath (Posix.asAbsFile "/tmp/someDir/myfile.txt") == (True, ["tmp","someDir"], Just "myfile.txt")
makeRelative :: FileDirClass fd => AbsDir -> AbsPath fd -> RelPath fd Source
This function can be used to construct a relative path by removing
the supplied AbsDir
from the front. It is a runtime error
if the
supplied AbsPath
doesn't start with the AbsDir
.
> Posix.makeRelative "/tmp/somedir" "/tmp/somedir/anotherdir/file.txt" == Posix.asRelFile "anotherdir/file.txt" > Posix.makeRelative "/tmp/somedir" "/tmp/somedir/anotherdir/dir" == Posix.asRelDir "anotherdir/dir" > Windows.makeRelative "c:\\tmp\\somedir" "c:\\Tmp\\SomeDir\\AnotherDir\\File.txt" == Windows.asRelFile "AnotherDir\\File.txt" > Windows.makeRelative "c:\\tmp\\somedir" "c:\\tmp\\somedir\\anotherdir\\dir" == Windows.asRelDir "anotherdir\\dir"
makeAbsolute :: AbsDir -> RelPath fd -> AbsPath fd Source
Joins an absolute directory with a relative path to construct a new absolute path.
> Posix.makeAbsolute "/tmp" "file.txt" == Posix.asAbsFile "/tmp/file.txt" > Posix.makeAbsolute "/tmp" "adir/file.txt" == Posix.asAbsFile "/tmp/adir/file.txt" > Posix.makeAbsolute "/tmp" "adir/dir" == Posix.asAbsDir "/tmp/adir/dir"
makeAbsoluteFromCwd :: RelPath fd -> IO (AbsPath fd) Source
Converts a relative path into an absolute one by prepending the current working directory.
genericMakeAbsolute :: AbsRelClass ar => AbsDir -> Path ar fd -> AbsPath fd Source
As for makeAbsolute
, but for use when the path may already be
absolute (in which case it is left unchanged).
> Posix.genericMakeAbsolute "/tmp" (Posix.asRelFile "file.txt") == "/tmp/file.txt" > Posix.genericMakeAbsolute "/tmp" (Posix.asRelFile "adir/file.txt") == "/tmp/adir/file.txt" > Posix.genericMakeAbsolute "/tmp" (Posix.asAbsFile "adir/file.txt") == "/adir/file.txt" > Posix.genericMakeAbsolute "/tmp" (Posix.asAbsFile "/adir/file.txt") == "/adir/file.txt"
genericMakeAbsoluteFromCwd :: AbsRelClass ar => Path ar fd -> IO (AbsPath fd) Source
As for makeAbsoluteFromCwd
, but for use when the path may already be
absolute (in which case it is left unchanged).
pathMap :: FileDirClass fd => (String -> String) -> Path ar fd -> Path ar fd Source
Map over the components of the path.
> Posix.pathMap (map toLower) "/tmp/Reports/SpreadSheets" == Posix.asAbsDir "/tmp/reports/spreadsheets"
Path Predicates
isAbsolute :: AbsRelClass ar => Path ar fd -> Bool Source
Test whether a
is absolute.Path
ar fd
> Posix.isAbsolute (Posix.asAbsFile "fred") > Posix.isAbsolute (Posix.asAbsFile "/fred") > Windows.isAbsolute (Windows.asAbsFile "\\fred") > Windows.isAbsolute (Windows.asAbsFile "c:\\fred")
isAbsoluteString :: String -> Bool Source
isRelative :: AbsRelClass ar => Path ar fd -> Bool Source
Invariant - this should return True iff arg is of type Path
Rel _
isRelative = not . isAbsolute > Posix.isRelative (Posix.asRelFile "fred") > Posix.isRelative (Posix.asRelFile "/fred") > Windows.isRelative (Windows.asRelFile "fred")
isRelativeString :: String -> Bool Source
hasAnExtension :: FilePath ar -> Bool Source
Does the given filename have an extension?
> null (Posix.takeExtension x) == not (Posix.hasAnExtension x)
hasExtension :: String -> FilePath ar -> Bool Source
Does the given filename have the given extension?
> Posix.hasExtension ".hs" (Posix.asRelFile "MyCode.hs") > Posix.hasExtension ".hs" (Posix.asRelFile "MyCode.bak.hs") > not $ Posix.hasExtension ".hs" (Posix.asRelFile "MyCode.hs.bak")
Separators
addTrailingPathSeparator :: String -> String Source
This is largely for FilePath
compatability
dropTrailingPathSeparator :: String -> String Source
This is largely for FilePath
compatability
File extension character
> Posix.extSeparator == '.'
hasTrailingPathSeparator :: String -> Bool Source
This is largely for FilePath
compatability
The character that separates directories. In the case where more than
one character is possible, pathSeparator
is the 'ideal' one.
> Posix.isPathSeparator Posix.pathSeparator
pathSeparators :: [Char] Source
The list of all possible separators.
> Posix.pathSeparator `elem` Posix.pathSeparators
searchPathSeparator :: Char Source
The character that is used to separate the entries in the $PATH environment variable.
isExtSeparator :: Char -> Bool Source
Is the character an extension character?
> Posix.isExtSeparator a == (a == Posix.extSeparator)
isPathSeparator :: Char -> Bool Source
Rather than using (==
, use this. Test if something
is a path separator.pathSeparator
)
> Posix.isPathSeparator a == (a `elem` Posix.pathSeparators)
isSearchPathSeparator :: Char -> Bool Source
Is the character a file separator?
> Posix.isSearchPathSeparator a == (a == Posix.searchPathSeparator)
Generic Manipulation Functions
genericAddExtension :: FileDirClass fd => Path ar fd -> String -> Path ar fd Source
This is a more flexible variant of addExtension
/ <.>
which can
work with files or directories
> Posix.genericAddExtension "/" "x" == Posix.asAbsDir "/.x" > Posix.genericAddExtension "/a" "x" == Posix.asAbsDir "/a.x" > Posix.genericAddExtension "" "x" == Posix.asRelFile ".x" > Posix.genericAddExtension "" "" == Posix.asRelFile ""
genericDropExtension :: FileDirClass fd => Path ar fd -> Path ar fd Source
genericDropExtensions :: FileDirClass fd => Path ar fd -> Path ar fd Source
genericSplitExtension :: FileDirClass fd => Path ar fd -> (Path ar fd, String) Source
genericSplitExtensions :: FileDirClass fd => Path ar fd -> (Path ar fd, String) Source
genericTakeExtension :: FileDirClass fd => Path ar fd -> String Source
genericTakeExtensions :: FileDirClass fd => Path ar fd -> String Source