packages feed

hpath 0.10.2 → 0.11.0

raw patch · 5 files changed

+127/−143 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- HPath: any :: QuasiQuoter
- HPath: class RelC m
- HPath: data Fn
- HPath: fn :: QuasiQuoter
- HPath: instance HPath.RelC HPath.Fn
- HPath: instance HPath.RelC HPath.Rel
- HPath: parseFn :: MonadThrow m => ByteString -> m (Path Fn)
- HPath: withFnPath :: Path Fn -> (ByteString -> IO a) -> IO a
- HPath.Internal: MkPath :: ByteString -> Path b
- HPath.Internal: data Path b
- HPath.Internal: instance Control.DeepSeq.NFData (HPath.Internal.Path b)
- HPath.Internal: instance GHC.Classes.Eq (HPath.Internal.Path b)
- HPath.Internal: instance GHC.Classes.Ord (HPath.Internal.Path b)
- HPath.Internal: instance GHC.Show.Show (HPath.Internal.Path b)
+ HPath: fromAny :: Either (Path Abs) (Path Rel) -> ByteString
+ HPath: getAllComponents :: Path Rel -> [Path Rel]
+ HPath: getAllComponentsAfterRoot :: Path Abs -> [Path Rel]
+ HPath: isRootPath :: Path Abs -> Bool
+ HPath: rootPath :: Path Abs
- HPath: (</>) :: RelC r => Path b -> Path r -> Path b
+ HPath: (</>) :: Path b -> Path Rel -> Path b
- HPath: basename :: MonadThrow m => Path b -> m (Path Fn)
+ HPath: basename :: MonadThrow m => Path b -> m (Path Rel)
- HPath: fromRel :: RelC r => Path r -> ByteString
+ HPath: fromRel :: Path Rel -> ByteString
- HPath: parseAny :: MonadThrow m => ByteString -> m (Path a)
+ HPath: parseAny :: MonadThrow m => ByteString -> m (Either (Path Abs) (Path Rel))

Files

CHANGELOG view
@@ -1,3 +1,10 @@+0.11.0+	* Many API breaking changes+	* Remove RelC and Fn, because they complicate API/break semantics (see #29)+	* Redo 'parseAny'+	* Unexpose HPath.Internal+	* Don't preserve trailing path separators (if you need to pass something to a C function that way, do it manually)+	* Added `rooPath`, `isRootPath`, `getAllComponents`, `getAllComponentsAfterRoot` 0.10.2 	* Add `parseAny` and the related QuasiQuoter 0.10.1
README.md view
@@ -28,11 +28,9 @@ ## Differences to 'path'  * doesn't attempt to fake IO-related information into the path, so whether a path points to a file or directory is up to your IO-code to decide...-* trailing path separators will be preserved if they exist, no messing with that * uses safe ByteString for filepaths under the hood instead of unsafe String * fixes broken [dirname](https://github.com/chrisdone/path/issues/18) * renames dirname/filename to basename/dirname to match the POSIX shell functions-* introduces a new `Path Fn` for safe filename guarantees and a `RelC` class * allows pattern matching via unidirectional PatternSynonym * uses simple doctest for testing * allows `~/` as relative path, because on posix level `~` is just a regular filename that does _NOT_ point to `$HOME`
hpath.cabal view
@@ -1,5 +1,5 @@ name:                hpath-version:             0.10.2+version:             0.11.0 synopsis:            Support for well-typed paths description:         Support for well-typed paths, utilizing ByteString under the hood. license:             BSD3@@ -30,7 +30,7 @@   else     ghc-options:       -Wall   exposed-modules:   HPath-                     HPath.Internal+  other-modules:     HPath.Internal   build-depends:     base >= 4.8 && <5                    , bytestring >= 0.10.0.0                    , deepseq
src/HPath.hs view
@@ -25,39 +25,39 @@    Abs   ,Path   ,Rel-  ,Fn   ,PathParseException   ,PathException-  ,RelC #if __GLASGOW_HASKELL__ >= 708   -- * PatternSynonyms/ViewPatterns   ,pattern Path #endif-   -- * Path Parsing+   -- * Path Construction   ,parseAbs-  ,parseFn   ,parseRel   ,parseAny+  ,rootPath   -- * Path Conversion   ,fromAbs   ,fromRel   ,toFilePath+  ,fromAny   -- * Path Operations   ,(</>)   ,basename   ,dirname-  ,isParentOf   ,getAllParents+  ,getAllComponents+  ,getAllComponentsAfterRoot   ,stripDir+  -- * Path Examination+  ,isParentOf+  ,isRootPath   -- * Path IO helpers   ,withAbsPath   ,withRelPath-  ,withFnPath   -- * Quasiquoters   ,abs   ,rel-  ,fn-  ,any   )   where @@ -91,14 +91,10 @@ -- | A relative path; one without a root. data Rel deriving (Typeable) --- | A filename, without any '/'.-data Fn deriving (Typeable)- -- | Exception when parsing a location. data PathParseException   = InvalidAbs ByteString   | InvalidRel ByteString-  | InvalidFn ByteString   | Couldn'tStripPrefixTPS ByteString ByteString   deriving (Show,Typeable) instance Exception PathParseException@@ -107,11 +103,7 @@   deriving (Show,Typeable) instance Exception PathException -class RelC m -instance RelC Rel-instance RelC Fn- -------------------------------------------------------------------------------- -- PatternSynonyms @@ -138,7 +130,7 @@ -- >>> parseAbs "/abc/def"      :: Maybe (Path Abs) -- Just "/abc/def" -- >>> parseAbs "/abc/def/.///" :: Maybe (Path Abs)--- Just "/abc/def/"+-- Just "/abc/def" -- >>> parseAbs "abc"           :: Maybe (Path Abs) -- Nothing -- >>> parseAbs ""              :: Maybe (Path Abs)@@ -151,7 +143,7 @@   if isAbsolute filepath &&      isValid filepath &&      not (hasParentDir filepath)-     then return (MkPath $ normalise filepath)+     then return (MkPath . dropTrailingPathSeparator . normalise $ filepath)      else throwM (InvalidAbs filepath)  @@ -166,11 +158,11 @@ -- >>> parseRel "abc"        :: Maybe (Path Rel) -- Just "abc" -- >>> parseRel "def/"       :: Maybe (Path Rel)--- Just "def/"+-- Just "def" -- >>> parseRel "abc/def"    :: Maybe (Path Rel) -- Just "abc/def" -- >>> parseRel "abc/def/."  :: Maybe (Path Rel)--- Just "abc/def/"+-- Just "abc/def" -- >>> parseRel "/abc"       :: Maybe (Path Rel) -- Nothing -- >>> parseRel ""           :: Maybe (Path Rel)@@ -189,46 +181,11 @@      filepath /= BS.pack [_period, _period] &&      not (hasParentDir filepath) &&      isValid filepath-     then return (MkPath $ normalise filepath)+     then return (MkPath . dropTrailingPathSeparator . normalise $ filepath)      else throwM (InvalidRel filepath)  --- | Parses a filename. Filenames must not contain slashes.--- Excludes '.' and '..'.------ Throws: 'PathParseException'------ >>> parseFn "abc"        :: Maybe (Path Fn)--- Just "abc"--- >>> parseFn "..."        :: Maybe (Path Fn)--- Just "..."--- >>> parseFn "def/"       :: Maybe (Path Fn)--- Nothing--- >>> parseFn "abc/def"    :: Maybe (Path Fn)--- Nothing--- >>> parseFn "abc/def/."  :: Maybe (Path Fn)--- Nothing--- >>> parseFn "/abc"       :: Maybe (Path Fn)--- Nothing--- >>> parseFn ""           :: Maybe (Path Fn)--- Nothing--- >>> parseFn "abc/../foo" :: Maybe (Path Fn)--- Nothing--- >>> parseFn "."          :: Maybe (Path Fn)--- Nothing--- >>> parseFn ".."         :: Maybe (Path Fn)--- Nothing-parseFn :: MonadThrow m-        => ByteString -> m (Path Fn)-parseFn filepath =-  if isFileName filepath &&-     filepath /= BS.singleton _period &&-     filepath /= BS.pack [_period, _period] &&-     isValid filepath-     then return (MkPath filepath)-     else throwM (InvalidFn filepath) - -- | Parses a path, whether it's relative or absolute. Will lose -- information on whether it's relative or absolute. If you need to know, -- reparse it.@@ -238,32 +195,36 @@ -- -- Throws: 'PathParseException' ----- >>> parseAny "/abc"       :: Maybe (Path a)--- Just "/abc"--- >>> parseAny "..."        :: Maybe (Path a)--- Just "..."--- >>> parseAny "abc/def"    :: Maybe (Path a)--- Just "abc/def"--- >>> parseAny "abc/def/."  :: Maybe (Path a)--- Just "abc/def/"--- >>> parseAny "/abc"       :: Maybe (Path a)--- Just "/abc"--- >>> parseAny ""           :: Maybe (Path a)+-- >>> 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 (Path a)+-- >>> parseAny "abc/../foo" :: Maybe (Either (Path Abs) (Path Rel)) -- Nothing--- >>> parseAny "."          :: Maybe (Path a)+-- >>> parseAny "."          :: Maybe (Either (Path Abs) (Path Rel)) -- Nothing--- >>> parseAny ".."         :: Maybe (Path a)+-- >>> parseAny ".."         :: Maybe (Either (Path Abs) (Path Rel)) -- Nothing-parseAny :: MonadThrow m => ByteString -> m (Path a)+parseAny :: MonadThrow m => ByteString -> m (Either (Path Abs) (Path Rel)) parseAny filepath = case parseAbs filepath of-  Just (MkPath p) -> pure $ (MkPath p)+  Just p -> pure $ Left p   Nothing         -> case parseRel filepath of-    Just (MkPath p) -> pure $ (MkPath p)-    Nothing         -> throwM (InvalidRel filepath)+    Just p -> pure $ Right p+    Nothing       -> throwM (InvalidRel filepath)  +rootPath :: Path Abs+rootPath = (MkPath (BS.singleton _slash))++ -------------------------------------------------------------------------------- -- Path Conversion @@ -276,10 +237,13 @@ fromAbs = toFilePath  -- | Convert a relative Path to a ByteString type.-fromRel :: RelC r => Path r -> ByteString+fromRel :: Path Rel -> ByteString fromRel = toFilePath +fromAny :: Either (Path Abs) (Path Rel) -> ByteString+fromAny = either toFilePath toFilePath + -------------------------------------------------------------------------------- -- Path Operations @@ -298,15 +262,16 @@ -- "/path/to/file" -- >>> (MkPath "/")        </> (MkPath "file/lal" :: Path Rel) -- "/file/lal"--- >>> (MkPath "/")        </> (MkPath "file/"    :: Path Rel)--- "/file/"-(</>) :: RelC r => Path b -> Path r -> Path b+-- >>> (MkPath "/")        </> (MkPath "file"    :: Path Rel)+-- "/file"+(</>) :: Path b -> Path Rel -> Path b (</>) (MkPath a) (MkPath b) = MkPath (a' `BS.append` b)   where-    a' = if BS.last a == pathSeparator-         then a-         else addTrailingPathSeparator a+    a' = if hasTrailingPathSeparator a+            then a+            else addTrailingPathSeparator a + -- | Strip directory from path, making it relative to that directory. -- Throws 'Couldn'tStripPrefixDir' if directory is not a parent of the path. --@@ -333,27 +298,13 @@   where     p' = addTrailingPathSeparator p --- | 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")--- True--- >>> (MkPath "lal/lad")      `isParentOf` (MkPath "lal/lad/fad")--- True--- >>> (MkPath "/")            `isParentOf` (MkPath "/")--- False--- >>> (MkPath "/lal/lad/fad") `isParentOf` (MkPath "/lal/lad")--- False--- >>> (MkPath "fad")          `isParentOf` (MkPath "fad")--- False-isParentOf :: Path b -> Path b -> Bool-isParentOf p l = isJust (stripDir p l :: Maybe (Path Rel)) - -- |Get all parents of a path. -- -- >>> getAllParents (MkPath "/abs/def/dod") -- ["/abs/def","/abs","/"]+-- >>> getAllParents (MkPath "/foo")+-- ["/"] -- >>> getAllParents (MkPath "/") -- [] getAllParents :: Path Abs -> [Path Abs]@@ -361,9 +312,29 @@   | np == BS.singleton pathSeparator = []   | otherwise = dirname (MkPath np) : getAllParents (dirname $ MkPath np)   where-    np = dropTrailingPathSeparator . normalise $ p+    np = normalise p  +-- | Gets all path components.+--+-- >>> getAllComponents (MkPath "abs/def/dod")+-- ["abs","def","dod"]+-- >>> getAllComponents (MkPath "abs")+-- ["abs"]+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")+-- ["abs","def","dod"]+-- >>> getAllComponentsAfterRoot (MkPath "/abs")+-- ["abs"]+getAllComponentsAfterRoot :: Path Abs -> [Path Rel]+getAllComponentsAfterRoot p = getAllComponents (fromJust $ stripDir rootPath p)++ -- | Extract the directory name of a path. -- -- >>> dirname (MkPath "/abc/def/dod")@@ -371,7 +342,7 @@ -- >>> dirname (MkPath "/") -- "/" dirname :: Path Abs -> Path Abs-dirname (MkPath fp) = MkPath (takeDirectory $ dropTrailingPathSeparator fp)+dirname (MkPath fp) = MkPath (takeDirectory fp)  -- | Extract the file part of a path. --@@ -382,21 +353,54 @@ -- -- Throws: `PathException` if given the root path "/" ----- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn)+-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Rel) -- Just "dod"--- >>> basename (MkPath "/abc/def/dod/") :: Maybe (Path Fn)+-- >>> basename (MkPath "abc/def/dod")  :: Maybe (Path Rel) -- Just "dod"--- >>> basename (MkPath "/")            :: Maybe (Path Fn)+-- >>> basename (MkPath "dod")          :: Maybe (Path Rel)+-- Just "dod"+-- >>> basename (MkPath "/")            :: Maybe (Path Rel) -- Nothing-basename :: MonadThrow m => Path b -> m (Path Fn)+basename :: MonadThrow m => Path b -> m (Path Rel) basename (MkPath l)   | not (isAbsolute rl) = return $ MkPath rl   | otherwise           = throwM RootDirHasNoBasename   where-    rl = last . splitPath . dropTrailingPathSeparator $ l+    rl = last . splitPath $ l  + --------------------------------------------------------------------------------+-- Path Examination++-- | 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")+-- True+-- >>> (MkPath "lal/lad")      `isParentOf` (MkPath "lal/lad/fad")+-- True+-- >>> (MkPath "/")            `isParentOf` (MkPath "/")+-- False+-- >>> (MkPath "/lal/lad/fad") `isParentOf` (MkPath "/lal/lad")+-- False+-- >>> (MkPath "fad")          `isParentOf` (MkPath "fad")+-- False+isParentOf :: Path b -> Path b -> Bool+isParentOf p l = isJust (stripDir p l :: Maybe (Path Rel))+++-- | Check whether the given Path is the root "/" path.+--+-- >>> isRootPath (MkPath "/lal/lad")+-- False+-- >>> isRootPath (MkPath "/")+-- True+isRootPath :: Path Abs -> Bool+isRootPath = (== rootPath)+++-------------------------------------------------------------------------------- -- Path IO helpers  @@ -408,10 +412,7 @@ withRelPath (MkPath p) action = action p  -withFnPath :: Path Fn -> (ByteString -> IO a) -> IO a-withFnPath (MkPath p) action = action p - ------------------------ -- ByteString helpers @@ -447,12 +448,6 @@ mkRel :: ByteString -> Q Exp mkRel = either (error . show) lift . parseRel -mkFN :: ByteString -> Q Exp-mkFN = either (error . show) lift . parseFn--mkAny :: ByteString -> Q Exp-mkAny = either (error . show) lift . parseAny- -- | Quasiquote an absolute Path. This accepts Unicode Chars and will encode as UTF-8. -- -- >>> [abs|/etc/profile|] :: Path Abs@@ -475,23 +470,3 @@ rel :: QuasiQuoter rel = qq mkRel --- | Quasiquote a file name. This accepts Unicode Chars and will encode as UTF-8.------ >>> [fn|etc|] :: Path Fn--- "etc"--- >>> [fn||] :: Path Fn--- "\239\131\144"-fn :: QuasiQuoter-fn = qq mkFN---- | Quasiquote any path (relative or absolute).--- This accepts Unicode Chars and will encode as UTF-8.------ >>> [any|/etc/profile|] :: Path a--- "/etc/profile"--- >>> [any|etc|] :: Path a--- "etc"--- >>> [any||] :: Path a--- "\239\131\144"-any :: QuasiQuoter-any = qq mkAny
src/HPath/Internal.hs view
@@ -10,15 +10,19 @@ import Data.ByteString (ByteString) import Data.Data --- | Path of some base and type.+-- | The main Path type. ----- Internally is a ByteString. The ByteString can be of two formats only:+-- The type variable 'b' is either: ----- 1. without trailing path separator: @file.txt@, @foo\/bar.txt@, @\/foo\/bar.txt@--- 2. with trailing path separator: @foo\/@, @\/foo\/bar\/@+--   * Abs -- absolute path+--   * Rel -- relative path ----- There are no duplicate--- path separators @\/\/@, no @..@, no @.\/@, no @~\/@, etc.+-- Internally is a ByteString. The path is guaranteed to+-- be normalised and contain no trailing Path separators,+-- except for the '/' root path.+--+-- There are no duplicate path separators+-- @\/\/@, no @..@, no @.\/@, no @~\/@, etc. data Path b = MkPath ByteString   deriving (Typeable)