hpath 0.12.0 → 0.12.1
raw patch · 4 files changed
+152/−124 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ HPath: basename' :: Path Rel -> Path Rel
Files
- CHANGELOG +4/−0
- hpath.cabal +1/−1
- src/HPath.hs +134/−118
- src/HPath/Internal.hs +13/−5
CHANGELOG view
@@ -1,3 +1,7 @@+0.12.1+ * Fix bug in "stripDir" and "isParentOf"+ * add total basename' variant for relative dirs+ * Improve documentation 0.12.0 * Type-safe quasi-quoters wrt [MR #39](https://github.com/hasufell/hpath/pull/39) * Allow "." as relative dir wrt #36
hpath.cabal view
@@ -1,5 +1,5 @@ name: hpath-version: 0.12.0+version: 0.12.1 synopsis: Support for well-typed paths description: Support for well-typed paths, utilizing ByteString under the hood. license: BSD3
src/HPath.hs view
@@ -23,8 +23,8 @@ module HPath ( -- * Types- Abs- ,Path+ Path+ ,Abs ,Rel ,PathParseException ,PathException@@ -46,6 +46,7 @@ -- * Path Operations ,(</>) ,basename+ ,basename' ,dirname ,getAllParents ,getAllComponents@@ -64,7 +65,6 @@ ) where -import Control.Applicative ((<|>)) import Control.Exception (Exception) import Control.Monad.Catch (MonadThrow(..)) #if MIN_VERSION_bytestring(0,10,8)@@ -128,20 +128,20 @@ -- -- Throws: 'PathParseException' ----- >>> parseAbs "/abc" :: Maybe (Path Abs)--- Just "/abc"--- >>> parseAbs "/" :: Maybe (Path Abs)--- Just "/"--- >>> parseAbs "/abc/def" :: Maybe (Path Abs)--- Just "/abc/def"--- >>> parseAbs "/abc/def/.///" :: Maybe (Path Abs)--- Just "/abc/def"--- >>> parseAbs "abc" :: Maybe (Path Abs)--- Nothing--- >>> parseAbs "" :: Maybe (Path Abs)--- Nothing--- >>> parseAbs "/abc/../foo" :: Maybe (Path Abs)--- Nothing+-- >>> parseAbs "/abc"+-- "/abc"+-- >>> parseAbs "/"+-- "/"+-- >>> parseAbs "/abc/def"+-- "/abc/def"+-- >>> parseAbs "/abc/def/.///"+-- "/abc/def"+-- >>> parseAbs "abc"+-- *** Exception: InvalidAbs "abc"+-- >>> parseAbs ""+-- *** Exception: InvalidAbs ""+-- >>> parseAbs "/abc/../foo"+-- *** Exception: InvalidAbs "/abc/../foo" parseAbs :: MonadThrow m => ByteString -> m (Path Abs) parseAbs filepath =@@ -155,33 +155,33 @@ -- | Get a location for a relative path. Produces a normalised -- path. ----- Note that @filepath@ may contain any number of @./@.--- It also may not contain a single @..@ anywhere.+-- Note that @filepath@ may contain any number of @./@,+-- but not a single @..@ anywhere. -- -- Throws: 'PathParseException' ----- >>> parseRel "abc" :: Maybe (Path Rel)--- Just "abc"--- >>> parseRel "def/" :: Maybe (Path Rel)--- Just "def"--- >>> parseRel "abc/def" :: Maybe (Path Rel)--- Just "abc/def"--- >>> parseRel "abc/def/." :: Maybe (Path Rel)--- Just "abc/def"--- >>> parseRel "/abc" :: Maybe (Path Rel)--- Nothing--- >>> parseRel "" :: Maybe (Path Rel)--- Nothing--- >>> parseRel "abc/../foo" :: Maybe (Path Rel)--- Nothing--- >>> parseRel "." :: Maybe (Path Rel)--- Just "."--- >>> parseRel "././././." :: Maybe (Path Rel)--- Just "."--- >>> parseRel "./..." :: Maybe (Path Rel)--- Just "..."--- >>> parseRel ".." :: Maybe (Path Rel)--- Nothing+-- >>> parseRel "abc"+-- "abc"+-- >>> parseRel "def/"+-- "def"+-- >>> parseRel "abc/def"+-- "abc/def"+-- >>> parseRel "abc/def/."+-- "abc/def"+-- >>> parseRel "/abc"+-- *** Exception: InvalidRel "/abc"+-- >>> parseRel ""+-- *** Exception: InvalidRel ""+-- >>> parseRel "abc/../foo"+-- *** Exception: InvalidRel "abc/../foo"+-- >>> parseRel "."+-- "."+-- >>> parseRel "././././."+-- "."+-- >>> parseRel "./..."+-- "..."+-- >>> parseRel ".."+-- *** Exception: InvalidRel ".." parseRel :: MonadThrow m => ByteString -> m (Path Rel) parseRel filepath =@@ -196,28 +196,26 @@ -- | Parses a path, whether it's relative or absolute. ----- Excludes '.' and '..'.--- -- Throws: 'PathParseException' ----- >>> parseAny "/abc" :: Maybe (Either (Path Abs) (Path Rel))--- Just (Left "/abc")--- >>> parseAny "..." :: Maybe (Either (Path Abs) (Path Rel))--- Just (Right "...")--- >>> parseAny "abc/def" :: Maybe (Either (Path Abs) (Path Rel))--- Just (Right "abc/def")--- >>> parseAny "abc/def/." :: Maybe (Either (Path Abs) (Path Rel))--- Just (Right "abc/def")--- >>> parseAny "/abc" :: Maybe (Either (Path Abs) (Path Rel))--- Just (Left "/abc")--- >>> parseAny "" :: Maybe (Either (Path Abs) (Path Rel))--- Nothing--- >>> parseAny "abc/../foo" :: Maybe (Either (Path Abs) (Path Rel))--- Nothing--- >>> parseAny "." :: Maybe (Either (Path Abs) (Path Rel))--- Just (Right ".")--- >>> parseAny ".." :: Maybe (Either (Path Abs) (Path Rel))--- Nothing+-- >>> parseAny "/abc"+-- Left "/abc"+-- >>> parseAny "..."+-- Right "..."+-- >>> parseAny "abc/def"+-- Right "abc/def"+-- >>> parseAny "abc/def/."+-- Right "abc/def"+-- >>> parseAny "/abc"+-- Left "/abc"+-- >>> parseAny ""+-- *** Exception: InvalidRel ""+-- >>> parseAny "abc/../foo"+-- *** Exception: InvalidRel "abc/../foo"+-- >>> parseAny "."+-- Right "."+-- >>> parseAny ".."+-- *** Exception: InvalidRel ".." parseAny :: MonadThrow m => ByteString -> m (Either (Path Abs) (Path Rel)) parseAny filepath = case parseAbs filepath of Just p -> pure $ Left p@@ -226,9 +224,11 @@ Nothing -> throwM (InvalidRel filepath) +-- | The @"/"@ root path. rootPath :: Path Abs rootPath = (MkPath (BS.singleton _slash)) +-- | The @"."@ pwd path. pwdPath :: Path Rel pwdPath = (MkPath (BS.singleton _period)) @@ -264,17 +264,15 @@ -- because this library is IO-agnostic and makes no assumptions about -- file types. ----- >>> (MkPath "/") </> (MkPath "file" :: Path Rel)+-- >>> [abs|/|] </> [rel|file|] -- "/file"--- >>> (MkPath "/path/to") </> (MkPath "file" :: Path Rel)+-- >>> [abs|/path/to|] </> [rel|file|] -- "/path/to/file"--- >>> (MkPath "/") </> (MkPath "file/lal" :: Path Rel)+-- >>> [abs|/|] </> [rel|file/lal|] -- "/file/lal"--- >>> (MkPath "/") </> (MkPath "file/" :: Path Rel)--- "/file"--- >>> (MkPath "/") </> (MkPath "." :: Path Rel)+-- >>> [abs|/|] </> [rel|.|] -- "/"--- >>> (MkPath ".") </> (MkPath "." :: Path Rel)+-- >>> [rel|.|] </> [rel|.|] -- "." (</>) :: Path b -> Path Rel -> Path b (</>) (MkPath a) (MkPath b) =@@ -286,37 +284,37 @@ -- -- The bases must match. ----- >>> (MkPath "/lal/lad") `stripDir` (MkPath "/lal/lad/fad") :: Maybe (Path Rel)--- Just "fad"--- >>> (MkPath "lal/lad") `stripDir` (MkPath "lal/lad/fad") :: Maybe (Path Rel)--- Just "fad"--- >>> (MkPath "/") `stripDir` (MkPath "/") :: Maybe (Path Rel)--- Just "."--- >>> (MkPath "/lal/lad/fad") `stripDir` (MkPath "/lal/lad") :: Maybe (Path Rel)--- Nothing--- >>> (MkPath "/abs") `stripDir` (MkPath "/lal/lad") :: Maybe (Path Rel)--- Nothing--- >>> (MkPath "fad") `stripDir` (MkPath "fad") :: Maybe (Path Rel)--- Just "."-stripDir :: MonadThrow m- => Path b -> Path b -> m (Path Rel)-stripDir (MkPath p) (MkPath l) =- case stripPrefix p' l <|> stripPrefix p l of- Nothing -> throwM (Couldn'tStripPrefixTPS p' l)- Just ok -> if BS.null ok- then return (MkPath $ BS.singleton _period)- else return (MkPath ok)- where- p' = addTrailingPathSeparator p+-- >>> [abs|/lal/lad|] `stripDir` [abs|/lal/lad/fad|]+-- "fad"+-- >>> [rel|lal/lad|] `stripDir` [rel|lal/lad/fad|]+-- "fad"+-- >>> [abs|/|] `stripDir` [abs|/|]+-- "."+-- >>> [abs|/lal/lad/fad|] `stripDir` [abs|/lal/lad|]+-- *** Exception: Couldn'tStripPrefixTPS "/lal/lad/fad" "/lal/lad"+-- >>> [abs|/abs|] `stripDir` [abs|/lal/lad|]+-- *** Exception: Couldn'tStripPrefixTPS "/abs" "/lal/lad"+-- >>> [rel|fad|] `stripDir` [rel|fad|]+-- "."+-- >>> [rel|.|] `stripDir` [rel|.|]+-- "."+-- >>> [rel|.|] `stripDir` [rel|.foo|]+-- *** Exception: Couldn'tStripPrefixTPS "." ".foo"+stripDir :: MonadThrow m => Path b -> Path b -> m (Path Rel)+stripDir (MkPath p) (MkPath l)+ | p == l = return pwdPath+ | otherwise = case stripPrefix (addTrailingPathSeparator p) l of+ Nothing -> throwM (Couldn'tStripPrefixTPS p l)+ Just ok -> return (MkPath ok) -- |Get all parents of a path. ----- >>> getAllParents (MkPath "/abs/def/dod")+-- >>> getAllParents [abs|/abs/def/dod|] -- ["/abs/def","/abs","/"]--- >>> getAllParents (MkPath "/foo")+-- >>> getAllParents [abs|/foo|] -- ["/"]--- >>> getAllParents (MkPath "/")+-- >>> getAllParents [abs|/|] -- [] getAllParents :: Path Abs -> [Path Abs] getAllParents (MkPath p)@@ -328,19 +326,21 @@ -- | Gets all path components. ----- >>> getAllComponents (MkPath "abs/def/dod")+-- >>> getAllComponents [rel|abs/def/dod|] -- ["abs","def","dod"]--- >>> getAllComponents (MkPath "abs")+-- >>> getAllComponents [rel|abs|] -- ["abs"]+-- >>> getAllComponents [rel|.|]+-- ["."] getAllComponents :: Path Rel -> [Path Rel] getAllComponents (MkPath p) = fmap MkPath . splitDirectories $ p -- | Gets all path components after the "/" root directory. ----- >>> getAllComponentsAfterRoot (MkPath "/abs/def/dod")+-- >>> getAllComponentsAfterRoot [abs|/abs/def/dod|] -- ["abs","def","dod"]--- >>> getAllComponentsAfterRoot (MkPath "/abs")+-- >>> getAllComponentsAfterRoot [abs|/abs|] -- ["abs"] getAllComponentsAfterRoot :: Path Abs -> [Path Rel] getAllComponentsAfterRoot p = getAllComponents (fromJust $ stripDir rootPath p)@@ -348,9 +348,9 @@ -- | Extract the directory name of a path. ----- >>> dirname (MkPath "/abc/def/dod")+-- >>> dirname [abs|/abc/def/dod|] -- "/abc/def"--- >>> dirname (MkPath "/")+-- >>> dirname [abs|/|] -- "/" dirname :: Path Abs -> Path Abs dirname (MkPath fp) = MkPath (takeDirectory fp)@@ -364,16 +364,16 @@ -- -- Throws: `PathException` if given the root path "/" ----- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Rel)--- Just "dod"--- >>> basename (MkPath "abc/def/dod") :: Maybe (Path Rel)--- Just "dod"--- >>> basename (MkPath "dod") :: Maybe (Path Rel)--- Just "dod"--- >>> basename (MkPath ".") :: Maybe (Path Rel)--- Just "."--- >>> basename (MkPath "/") :: Maybe (Path Rel)--- Nothing+-- >>> basename [abs|/abc/def/dod|]+-- "dod"+-- >>> basename [rel|abc/def/dod|]+-- "dod"+-- >>> basename [rel|dod|]+-- "dod"+-- >>> basename [rel|.|]+-- "."+-- >>> basename [abs|/|]+-- *** Exception: RootDirHasNoBasename basename :: MonadThrow m => Path b -> m (Path Rel) basename (MkPath l) | not (isAbsolute rl) = return $ MkPath rl@@ -381,6 +381,20 @@ where rl = last . splitPath $ l +-- | Extract the file part of a relative path.+--+-- The following properties hold:+--+-- @basename' (p \<\/> a) == basename' a@+--+-- >>> basename' [rel|abc/def/dod|]+-- "dod"+-- >>> basename' [rel|dod|]+-- "dod"+-- >>> basename' [rel|.|]+-- "."+basename' :: Path Rel -> Path Rel+basename' (MkPath l) = MkPath . last . splitPath $ l --------------------------------------------------------------------------------@@ -389,16 +403,18 @@ -- | Is p a parent of the given location? Implemented in terms of -- 'stripDir'. The bases must match. ----- >>> (MkPath "/lal/lad") `isParentOf` (MkPath "/lal/lad/fad")+-- >>> [abs|/lal/lad|] `isParentOf` [abs|/lal/lad/fad|] -- True--- >>> (MkPath "lal/lad") `isParentOf` (MkPath "lal/lad/fad")+-- >>> [rel|lal/lad|] `isParentOf` [rel|lal/lad/fad|] -- True--- >>> (MkPath "/") `isParentOf` (MkPath "/")+-- >>> [abs|/|] `isParentOf` [abs|/|] -- False--- >>> (MkPath "/lal/lad/fad") `isParentOf` (MkPath "/lal/lad")+-- >>> [abs|/lal/lad/fad|] `isParentOf` [abs|/lal/lad|] -- False--- >>> (MkPath "fad") `isParentOf` (MkPath "fad")+-- >>> [rel|fad|] `isParentOf` [rel|fad|] -- False+-- >>> [rel|.|] `isParentOf` [rel|.foo|]+-- False isParentOf :: Path b -> Path b -> Bool isParentOf p l = case stripDir p l :: Maybe (Path Rel) of Nothing -> False@@ -409,18 +425,18 @@ -- | Check whether the given Path is the root "/" path. ----- >>> isRootPath (MkPath "/lal/lad")+-- >>> isRootPath [abs|/lal/lad|] -- False--- >>> isRootPath (MkPath "/")+-- >>> isRootPath [abs|/|] -- True isRootPath :: Path Abs -> Bool isRootPath = (== rootPath) -- | Check whether the given Path is the pwd "." path. ----- >>> isPwdPath (MkPath "/lal/lad")+-- >>> isPwdPath [rel|lal/lad|] -- False--- >>> isPwdPath (MkPath ".")+-- >>> isPwdPath [rel|.|] -- True isPwdPath :: Path Rel -> Bool isPwdPath = (== pwdPath)
src/HPath/Internal.hs view
@@ -14,15 +14,23 @@ -- -- The type variable 'b' is either: ----- * Abs -- absolute path--- * Rel -- relative path+-- * 'HPath.Abs' -- absolute path (starting with a @"/"@)+-- * 'HPath.Rel' -- relative path (not starting with a @"/"@) ----- Internally is a ByteString. The path is guaranteed to+-- Internally it is a ByteString. The path is guaranteed to -- be normalised and contain no trailing Path separators,--- except for the '/' root path.+-- except for the @"/"@ root path. -- -- There are no duplicate path separators--- @\/\/@, no @..@, no @.\/@, no @~\/@, etc.+-- @"\/\/"@, no @".."@, no @".\/"@, etc.+-- +-- Two special paths exist:+--+-- * @"/"@ -- the 'HPath.rootPath' (absolute)+-- * @"."@ -- the 'HPath.pwdPath' (relative)+--+-- The constructor is not exposed. Instead, use the smart constructors+-- 'HPath.parseAbs', 'HPath.parseRel' and 'HPath.parseAny'. data Path b = MkPath ByteString deriving (Typeable)