Rearrange, prettify and improve haddock

This also matches the documentation order from the
filepath package more.
This commit is contained in:
Julian Ospald 2016-05-24 02:16:16 +02:00
parent eb72fce33f
commit 9c199c6da2
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
1 changed files with 164 additions and 116 deletions

View File

@ -20,7 +20,7 @@
module System.Posix.FilePath ( module System.Posix.FilePath (
-- * Separators -- * Separator predicates
pathSeparator pathSeparator
, isPathSeparator , isPathSeparator
, searchPathSeparator , searchPathSeparator
@ -32,20 +32,20 @@ module System.Posix.FilePath (
, splitSearchPath , splitSearchPath
, getSearchPath , getSearchPath
-- * File extensions -- * Extension functions
, splitExtension , splitExtension
, takeExtension , takeExtension
, replaceExtension , replaceExtension
, dropExtension , dropExtension
, addExtension , addExtension
, hasExtension , hasExtension
, stripExtension
, (<.>) , (<.>)
, splitExtensions , splitExtensions
, dropExtensions , dropExtensions
, takeExtensions , takeExtensions
, stripExtension
-- * Filenames/Directory names -- * Filename\/directory functions
, splitFileName , splitFileName
, takeFileName , takeFileName
, replaceFileName , replaceFileName
@ -54,29 +54,25 @@ module System.Posix.FilePath (
, replaceBaseName , replaceBaseName
, takeDirectory , takeDirectory
, replaceDirectory , replaceDirectory
-- * Path combinators and splitters
, combine , combine
, (</>) , (</>)
, splitPath , splitPath
, joinPath , joinPath
, splitDirectories , splitDirectories
-- * Path conversions -- * Trailing slash functions
, normalise
-- * Trailing path separator
, hasTrailingPathSeparator , hasTrailingPathSeparator
, addTrailingPathSeparator , addTrailingPathSeparator
, dropTrailingPathSeparator , dropTrailingPathSeparator
-- * Queries -- * File name manipulations
, normalise
, equalFilePath
, isRelative , isRelative
, isAbsolute , isAbsolute
, isValid , isValid
, isFileName , isFileName
, hasParentDir , hasParentDir
, equalFilePath
, hiddenFile , hiddenFile
, module System.Posix.ByteString.FilePath , module System.Posix.ByteString.FilePath
@ -105,39 +101,52 @@ import Control.Arrow (second)
-- >>> let _chr :: Word8 -> Char; _chr = chr . fromIntegral -- >>> let _chr :: Word8 -> Char; _chr = chr . fromIntegral
------------------------
-- Separator predicates
-- | Path separator character -- | Path separator character
pathSeparator :: Word8 pathSeparator :: Word8
pathSeparator = _slash pathSeparator = _slash
-- | Check if a character is the path separator -- | Check if a character is the path separator
-- --
-- prop> \n -> (_chr n == '/') == isPathSeparator n -- prop> \n -> (_chr n == '/') == isPathSeparator n
isPathSeparator :: Word8 -> Bool isPathSeparator :: Word8 -> Bool
isPathSeparator = (== pathSeparator) isPathSeparator = (== pathSeparator)
-- | Search path separator -- | Search path separator
searchPathSeparator :: Word8 searchPathSeparator :: Word8
searchPathSeparator = _colon searchPathSeparator = _colon
-- | Check if a character is the search path separator -- | Check if a character is the search path separator
-- --
-- prop> \n -> (_chr n == ':') == isSearchPathSeparator n -- prop> \n -> (_chr n == ':') == isSearchPathSeparator n
isSearchPathSeparator :: Word8 -> Bool isSearchPathSeparator :: Word8 -> Bool
isSearchPathSeparator = (== searchPathSeparator) isSearchPathSeparator = (== searchPathSeparator)
-- | File extension separator -- | File extension separator
extSeparator :: Word8 extSeparator :: Word8
extSeparator = _period extSeparator = _period
-- | Check if a character is the file extension separator -- | Check if a character is the file extension separator
-- --
-- prop> \n -> (_chr n == '.') == isExtSeparator n -- prop> \n -> (_chr n == '.') == isExtSeparator n
isExtSeparator :: Word8 -> Bool isExtSeparator :: Word8 -> Bool
isExtSeparator = (== extSeparator) isExtSeparator = (== extSeparator)
------------------------ ------------------------
-- $PATH methods -- $PATH methods
-- | Take a ByteString, split it on the 'searchPathSeparator'. -- | Take a ByteString, split it on the 'searchPathSeparator'.
-- Blank items are converted to @.@. -- Blank items are converted to @.@.
-- --
@ -161,12 +170,15 @@ splitSearchPath = f
| BS.null x = [BS.singleton _period] | BS.null x = [BS.singleton _period]
| otherwise = [x] | otherwise = [x]
-- | Get a list of 'RawFilePath's in the $PATH variable. -- | Get a list of 'RawFilePath's in the $PATH variable.
getSearchPath :: IO [RawFilePath] getSearchPath :: IO [RawFilePath]
getSearchPath = fmap (maybe [] splitSearchPath) (PE.getEnv $ fromString "PATH") getSearchPath = fmap (maybe [] splitSearchPath) (PE.getEnv $ fromString "PATH")
------------------------ ------------------------
-- extension stuff -- Extension functions
-- | Split a 'RawFilePath' into a path+filename and extension -- | Split a 'RawFilePath' into a path+filename and extension
-- --
@ -186,6 +198,7 @@ splitExtension x = if BS.null basename
(path,file) = splitFileNameRaw x (path,file) = splitFileNameRaw x
(basename,fileExt) = BS.breakEnd isExtSeparator file (basename,fileExt) = BS.breakEnd isExtSeparator file
-- | Get the final extension from a 'RawFilePath' -- | Get the final extension from a 'RawFilePath'
-- --
-- >>> takeExtension "file.exe" -- >>> takeExtension "file.exe"
@ -197,12 +210,14 @@ splitExtension x = if BS.null basename
takeExtension :: RawFilePath -> ByteString takeExtension :: RawFilePath -> ByteString
takeExtension = snd . splitExtension takeExtension = snd . splitExtension
-- | Change a file's extension -- | Change a file's extension
-- --
-- prop> \path -> let ext = takeExtension path in replaceExtension path ext == path -- prop> \path -> let ext = takeExtension path in replaceExtension path ext == path
replaceExtension :: RawFilePath -> ByteString -> RawFilePath replaceExtension :: RawFilePath -> ByteString -> RawFilePath
replaceExtension path ext = dropExtension path <.> ext replaceExtension path ext = dropExtension path <.> ext
-- | Drop the final extension from a 'RawFilePath' -- | Drop the final extension from a 'RawFilePath'
-- --
-- >>> dropExtension "file.exe" -- >>> dropExtension "file.exe"
@ -214,6 +229,7 @@ replaceExtension path ext = dropExtension path <.> ext
dropExtension :: RawFilePath -> RawFilePath dropExtension :: RawFilePath -> RawFilePath
dropExtension = fst . splitExtension dropExtension = fst . splitExtension
-- | Add an extension to a 'RawFilePath' -- | Add an extension to a 'RawFilePath'
-- --
-- >>> addExtension "file" ".exe" -- >>> addExtension "file" ".exe"
@ -229,10 +245,6 @@ addExtension file ext
| otherwise = BS.intercalate (BS.singleton extSeparator) [file, ext] | otherwise = BS.intercalate (BS.singleton extSeparator) [file, ext]
-- | Operator version of 'addExtension'
(<.>) :: RawFilePath -> ByteString -> RawFilePath
(<.>) = addExtension
-- | Check if a 'RawFilePath' has an extension -- | Check if a 'RawFilePath' has an extension
-- --
-- >>> hasExtension "file" -- >>> hasExtension "file"
@ -244,6 +256,43 @@ addExtension file ext
hasExtension :: RawFilePath -> Bool hasExtension :: RawFilePath -> Bool
hasExtension = isJust . BS.elemIndex extSeparator . takeFileName hasExtension = isJust . BS.elemIndex extSeparator . takeFileName
-- | Operator version of 'addExtension'
(<.>) :: RawFilePath -> ByteString -> RawFilePath
(<.>) = addExtension
-- | Split a 'RawFilePath' on the first extension.
--
-- >>> splitExtensions "/path/file.tar.gz"
-- ("/path/file",".tar.gz")
--
-- prop> \path -> uncurry addExtension (splitExtensions path) == path
splitExtensions :: RawFilePath -> (RawFilePath, ByteString)
splitExtensions x = if BS.null basename
then (path,fileExt)
else (BS.append path basename,fileExt)
where
(path,file) = splitFileNameRaw x
(basename,fileExt) = BS.break isExtSeparator file
-- | Remove all extensions from a 'RawFilePath'
--
-- >>> dropExtensions "/path/file.tar.gz"
-- "/path/file"
dropExtensions :: RawFilePath -> RawFilePath
dropExtensions = fst . splitExtensions
-- | Take all extensions from a 'RawFilePath'
--
-- >>> takeExtensions "/path/file.tar.gz"
-- ".tar.gz"
takeExtensions :: RawFilePath -> ByteString
takeExtensions = snd . splitExtensions
-- | Drop the given extension from a FilePath, and the @\".\"@ preceding it. -- | Drop the given extension from a FilePath, and the @\".\"@ preceding it.
-- Returns 'Nothing' if the FilePath does not have the given extension, or -- Returns 'Nothing' if the FilePath does not have the given extension, or
-- 'Just' and the part before the extension if it does. -- 'Just' and the part before the extension if it does.
@ -276,36 +325,11 @@ stripExtension bs path
then bs then bs
else extSeparator `BS.cons` bs else extSeparator `BS.cons` bs
-- | Split a 'RawFilePath' on the first extension.
--
-- >>> splitExtensions "/path/file.tar.gz"
-- ("/path/file",".tar.gz")
--
-- prop> \path -> uncurry addExtension (splitExtensions path) == path
splitExtensions :: RawFilePath -> (RawFilePath, ByteString)
splitExtensions x = if BS.null basename
then (path,fileExt)
else (BS.append path basename,fileExt)
where
(path,file) = splitFileNameRaw x
(basename,fileExt) = BS.break isExtSeparator file
-- | Remove all extensions from a 'RawFilePath'
--
-- >>> dropExtensions "/path/file.tar.gz"
-- "/path/file"
dropExtensions :: RawFilePath -> RawFilePath
dropExtensions = fst . splitExtensions
-- | Take all extensions from a 'RawFilePath'
--
-- >>> takeExtensions "/path/file.tar.gz"
-- ".tar.gz"
takeExtensions :: RawFilePath -> ByteString
takeExtensions = snd . splitExtensions
------------------------ ------------------------
-- more stuff -- Filename/directory functions
-- | Split a 'RawFilePath' into (path,file). 'combine' is the inverse -- | Split a 'RawFilePath' into (path,file). 'combine' is the inverse
-- --
@ -335,12 +359,14 @@ splitFileName x = if BS.null path
takeFileName :: RawFilePath -> RawFilePath takeFileName :: RawFilePath -> RawFilePath
takeFileName = snd . splitFileName takeFileName = snd . splitFileName
-- | Change the file name -- | Change the file name
-- --
-- prop> \path -> replaceFileName path (takeFileName path) == path -- prop> \path -> replaceFileName path (takeFileName path) == path
replaceFileName :: RawFilePath -> ByteString -> RawFilePath replaceFileName :: RawFilePath -> ByteString -> RawFilePath
replaceFileName x y = fst (splitFileNameRaw x) </> y replaceFileName x y = fst (splitFileNameRaw x) </> y
-- | Drop the file name -- | Drop the file name
-- --
-- >>> dropFileName "path/file.txt" -- >>> dropFileName "path/file.txt"
@ -350,6 +376,7 @@ replaceFileName x y = fst (splitFileNameRaw x) </> y
dropFileName :: RawFilePath -> RawFilePath dropFileName :: RawFilePath -> RawFilePath
dropFileName = fst . splitFileName dropFileName = fst . splitFileName
-- | Get the file name, without a trailing extension -- | Get the file name, without a trailing extension
-- --
-- >>> takeBaseName "path/file.tar.gz" -- >>> takeBaseName "path/file.tar.gz"
@ -359,6 +386,7 @@ dropFileName = fst . splitFileName
takeBaseName :: RawFilePath -> ByteString takeBaseName :: RawFilePath -> ByteString
takeBaseName = dropExtension . takeFileName takeBaseName = dropExtension . takeFileName
-- | Change the base name -- | Change the base name
-- --
-- >>> replaceBaseName "path/file.tar.gz" "bob" -- >>> replaceBaseName "path/file.tar.gz" "bob"
@ -371,6 +399,7 @@ replaceBaseName path name = combineRaw dir (name <.> ext)
(dir,file) = splitFileNameRaw path (dir,file) = splitFileNameRaw path
ext = takeExtension file ext = takeExtension file
-- | Get the directory, moving up one level if it's already a directory -- | Get the directory, moving up one level if it's already a directory
-- --
-- >>> takeDirectory "path/file.txt" -- >>> takeDirectory "path/file.txt"
@ -390,12 +419,14 @@ takeDirectory x = case () of
res = fst $ BS.spanEnd isPathSeparator file res = fst $ BS.spanEnd isPathSeparator file
file = dropFileName x file = dropFileName x
-- | Change the directory component of a 'RawFilePath' -- | Change the directory component of a 'RawFilePath'
-- --
-- prop> \path -> replaceDirectory path (takeDirectory path) `equalFilePath` path || takeDirectory path == "." -- prop> \path -> replaceDirectory path (takeDirectory path) `equalFilePath` path || takeDirectory path == "."
replaceDirectory :: RawFilePath -> ByteString -> RawFilePath replaceDirectory :: RawFilePath -> ByteString -> RawFilePath
replaceDirectory file dir = combineRaw dir (takeFileName file) replaceDirectory file dir = combineRaw dir (takeFileName file)
-- | Join two paths together -- | Join two paths together
-- --
-- >>> combine "/" "file" -- >>> combine "/" "file"
@ -408,6 +439,7 @@ combine :: RawFilePath -> RawFilePath -> RawFilePath
combine a b | not (BS.null b) && isPathSeparator (BS.head b) = b combine a b | not (BS.null b) && isPathSeparator (BS.head b) = b
| otherwise = combineRaw a b | otherwise = combineRaw a b
-- | Operator version of combine -- | Operator version of combine
(</>) :: RawFilePath -> RawFilePath -> RawFilePath (</>) :: RawFilePath -> RawFilePath -> RawFilePath
(</>) = combine (</>) = combine
@ -429,6 +461,17 @@ splitPath = splitter
Nothing -> [x] Nothing -> [x]
Just runlen -> uncurry (:) . second splitter $ BS.splitAt (ix+1+runlen) x Just runlen -> uncurry (:) . second splitter $ BS.splitAt (ix+1+runlen) x
-- | Join a split path back together
--
-- prop> \path -> joinPath (splitPath path) == path
--
-- >>> joinPath ["path","to","file.txt"]
-- "path/to/file.txt"
joinPath :: [RawFilePath] -> RawFilePath
joinPath = foldr (</>) BS.empty
-- | Like 'splitPath', but without trailing slashes -- | Like 'splitPath', but without trailing slashes
-- --
-- >>> splitDirectories "/path/to/file.txt" -- >>> splitDirectories "/path/to/file.txt"
@ -444,14 +487,60 @@ splitDirectories x
where where
splitter = filter (not . BS.null) . BS.split pathSeparator splitter = filter (not . BS.null) . BS.split pathSeparator
-- | Join a split path back together
------------------------
-- Trailing slash functions
-- | Check if the last character of a 'RawFilePath' is '/'.
-- --
-- prop> \path -> joinPath (splitPath path) == path -- >>> hasTrailingPathSeparator "/path/"
-- True
-- >>> hasTrailingPathSeparator "/"
-- True
-- >>> hasTrailingPathSeparator "/path"
-- False
hasTrailingPathSeparator :: RawFilePath -> Bool
hasTrailingPathSeparator x
| BS.null x = False
| otherwise = isPathSeparator $ BS.last x
-- | Add a trailing path separator.
-- --
-- >>> joinPath ["path","to","file.txt"] -- >>> addTrailingPathSeparator "/path"
-- "path/to/file.txt" -- "/path/"
joinPath :: [RawFilePath] -> RawFilePath -- >>> addTrailingPathSeparator "/path/"
joinPath = foldr (</>) BS.empty -- "/path/"
-- >>> addTrailingPathSeparator "/"
-- "/"
addTrailingPathSeparator :: RawFilePath -> RawFilePath
addTrailingPathSeparator x = if hasTrailingPathSeparator x
then x
else x `BS.snoc` pathSeparator
-- | Remove a trailing path separator
--
-- >>> dropTrailingPathSeparator "/path/"
-- "/path"
-- >>> dropTrailingPathSeparator "/path////"
-- "/path"
-- >>> dropTrailingPathSeparator "/"
-- "/"
-- >>> dropTrailingPathSeparator "//"
-- "/"
dropTrailingPathSeparator :: RawFilePath -> RawFilePath
dropTrailingPathSeparator x
| x == BS.singleton pathSeparator = x
| otherwise = if hasTrailingPathSeparator x
then dropTrailingPathSeparator $ BS.init x
else x
------------------------
-- File name manipulations
-- |Normalise a file. -- |Normalise a file.
@ -507,54 +596,36 @@ normalise filepath =
dropDots :: [ByteString] -> [ByteString] dropDots :: [ByteString] -> [ByteString]
dropDots = filter (BS.singleton _period /=) dropDots = filter (BS.singleton _period /=)
------------------------
-- trailing path separators
-- | Check if the last character of a 'RawFilePath' is '/'. -- |Equality of two filepaths. The filepaths are normalised
-- and trailing path separators are dropped.
-- --
-- >>> hasTrailingPathSeparator "/path/" -- >>> equalFilePath "foo" "foo"
-- True -- True
-- >>> hasTrailingPathSeparator "/" -- >>> equalFilePath "foo" "foo/"
-- True -- True
-- >>> hasTrailingPathSeparator "/path" -- >>> equalFilePath "foo" "./foo"
-- True
-- >>> equalFilePath "foo" "/foo"
-- False
-- >>> equalFilePath "foo" "FOO"
-- False
-- >>> equalFilePath "foo" "../foo"
-- False -- False
hasTrailingPathSeparator :: RawFilePath -> Bool
hasTrailingPathSeparator x
| BS.null x = False
| otherwise = isPathSeparator $ BS.last x
-- | Add a trailing path separator.
-- --
-- >>> addTrailingPathSeparator "/path" -- prop> \p -> equalFilePath p p
-- "/path/" equalFilePath :: RawFilePath -> RawFilePath -> Bool
-- >>> addTrailingPathSeparator "/path/" equalFilePath p1 p2 = f p1 == f p2
-- "/path/" where
-- >>> addTrailingPathSeparator "/" f x = dropTrailingPathSeparator $ normalise x
-- "/"
addTrailingPathSeparator :: RawFilePath -> RawFilePath
addTrailingPathSeparator x = if hasTrailingPathSeparator x
then x
else x `BS.snoc` pathSeparator
-- | Remove a trailing path separator
-- | Check if a path is relative
-- --
-- >>> dropTrailingPathSeparator "/path/" -- prop> \path -> isRelative path /= isAbsolute path
-- "/path" isRelative :: RawFilePath -> Bool
-- >>> dropTrailingPathSeparator "/path////" isRelative = not . isAbsolute
-- "/path"
-- >>> dropTrailingPathSeparator "/"
-- "/"
-- >>> dropTrailingPathSeparator "//"
-- "/"
dropTrailingPathSeparator :: RawFilePath -> RawFilePath
dropTrailingPathSeparator x
| x == BS.singleton pathSeparator = x
| otherwise = if hasTrailingPathSeparator x
then dropTrailingPathSeparator $ BS.init x
else x
------------------------
-- Filename/system stuff
-- | Check if a path is absolute -- | Check if a path is absolute
-- --
@ -569,11 +640,6 @@ isAbsolute x
| BS.length x > 0 = isPathSeparator (BS.head x) | BS.length x > 0 = isPathSeparator (BS.head x)
| otherwise = False | otherwise = False
-- | Check if a path is relative
--
-- prop> \path -> isRelative path /= isAbsolute path
isRelative :: RawFilePath -> Bool
isRelative = not . isAbsolute
-- | Is a FilePath valid, i.e. could you create a file like it? -- | Is a FilePath valid, i.e. could you create a file like it?
-- --
@ -589,6 +655,7 @@ isValid filepath
| _nul `BS.elem` filepath = False | _nul `BS.elem` filepath = False
| otherwise = True | otherwise = True
-- | Is the given path a valid filename? This includes -- | Is the given path a valid filename? This includes
-- "." and "..". -- "." and "..".
-- --
@ -610,6 +677,7 @@ isFileName filepath =
not (BS.null filepath) && not (BS.null filepath) &&
not (_nul `BS.elem` filepath) not (_nul `BS.elem` filepath)
-- | Check if the filepath has any parent directories in it. -- | Check if the filepath has any parent directories in it.
-- --
-- >>> hasParentDir "/.." -- >>> hasParentDir "/.."
@ -641,28 +709,6 @@ hasParentDir filepath =
where where
pathDoubleDot = BS.pack [_period, _period] pathDoubleDot = BS.pack [_period, _period]
-- |Equality of two filepaths. The filepaths are normalised
-- and trailing path separators are dropped.
--
-- >>> equalFilePath "foo" "foo"
-- True
-- >>> equalFilePath "foo" "foo/"
-- True
-- >>> equalFilePath "foo" "./foo"
-- True
-- >>> equalFilePath "foo" "/foo"
-- False
-- >>> equalFilePath "foo" "FOO"
-- False
-- >>> equalFilePath "foo" "../foo"
-- False
--
-- prop> \p -> equalFilePath p p
equalFilePath :: RawFilePath -> RawFilePath -> Bool
equalFilePath p1 p2 = f p1 == f p2
where
f x = dropTrailingPathSeparator $ normalise x
-- | Whether the file is a hidden file. -- | Whether the file is a hidden file.
-- --
@ -691,6 +737,8 @@ hiddenFile fp
where where
fn = takeFileName fp fn = takeFileName fp
------------------------ ------------------------
-- internal stuff -- internal stuff