hpath 0.11.0 → 0.12.0
raw patch · 4 files changed
+64/−25 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- HPath: instance Language.Haskell.TH.Syntax.Lift (HPath.Internal.Path a)
+ HPath: instance Data.Typeable.Internal.Typeable a => Language.Haskell.TH.Syntax.Lift (HPath.Internal.Path a)
+ HPath: isPwdPath :: Path Rel -> Bool
+ HPath: pwdPath :: Path Rel
Files
- CHANGELOG +3/−0
- README.md +5/−1
- hpath.cabal +1/−1
- src/HPath.hs +55/−23
CHANGELOG view
@@ -1,3 +1,6 @@+0.12.0+ * Type-safe quasi-quoters wrt [MR #39](https://github.com/hasufell/hpath/pull/39)+ * Allow "." as relative dir wrt #36 0.11.0 * Many API breaking changes * Remove RelC and Fn, because they complicate API/break semantics (see #29)
README.md view
@@ -4,7 +4,11 @@ Support for well-typed paths in Haskell. -This package is part of the HPath suite, also check out [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath) and [hpath-io](https://hackage.haskell.org/package/hpath-io).+This package is part of the HPath suite, also check out:++* [hpath-directory](https://hackage.haskell.org/package/hpath-directory)+* [hpath-filepath](https://hackage.haskell.org/package/hpath-filepath)+* [hpath-io](https://hackage.haskell.org/package/hpath-io) ## Motivation
hpath.cabal view
@@ -1,5 +1,5 @@ name: hpath-version: 0.11.0+version: 0.12.0 synopsis: Support for well-typed paths description: Support for well-typed paths, utilizing ByteString under the hood. license: BSD3
src/HPath.hs view
@@ -18,6 +18,7 @@ #endif {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ScopedTypeVariables #-} module HPath (@@ -36,6 +37,7 @@ ,parseRel ,parseAny ,rootPath+ ,pwdPath -- * Path Conversion ,fromAbs ,fromRel@@ -52,6 +54,7 @@ -- * Path Examination ,isParentOf ,isRootPath+ ,isPwdPath -- * Path IO helpers ,withAbsPath ,withRelPath@@ -61,6 +64,7 @@ ) where +import Control.Applicative ((<|>)) import Control.Exception (Exception) import Control.Monad.Catch (MonadThrow(..)) #if MIN_VERSION_bytestring(0,10,8)@@ -76,7 +80,8 @@ import Data.Word8 import HPath.Internal import Language.Haskell.TH-import Language.Haskell.TH.Syntax (Exp(..), Lift(..), lift)+import Language.Haskell.TH.Syntax (Lift(..), lift)+import qualified Language.Haskell.TH.Syntax as TH import Language.Haskell.TH.Quote (QuasiQuoter(..)) import Prelude hiding (abs, any) import System.Posix.FilePath hiding ((</>))@@ -150,8 +155,8 @@ -- | Get a location for a relative path. Produces a normalised -- path. ----- Note that @filepath@ may contain any number of @./@ but may not consist--- solely of @./@. It also may not contain a single @..@ anywhere.+-- Note that @filepath@ may contain any number of @./@.+-- It also may not contain a single @..@ anywhere. -- -- Throws: 'PathParseException' --@@ -170,14 +175,17 @@ -- >>> parseRel "abc/../foo" :: Maybe (Path Rel) -- Nothing -- >>> parseRel "." :: Maybe (Path Rel)--- Nothing+-- Just "."+-- >>> parseRel "././././." :: Maybe (Path Rel)+-- Just "."+-- >>> parseRel "./..." :: Maybe (Path Rel)+-- Just "..." -- >>> parseRel ".." :: Maybe (Path Rel) -- Nothing parseRel :: MonadThrow m => ByteString -> m (Path Rel) parseRel filepath = if not (isAbsolute filepath) &&- filepath /= BS.singleton _period && filepath /= BS.pack [_period, _period] && not (hasParentDir filepath) && isValid filepath@@ -186,11 +194,8 @@ --- | 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.+-- | Parses a path, whether it's relative or absolute. ----- Filenames must not contain slashes. -- Excludes '.' and '..'. -- -- Throws: 'PathParseException'@@ -210,7 +215,7 @@ -- >>> parseAny "abc/../foo" :: Maybe (Either (Path Abs) (Path Rel)) -- Nothing -- >>> parseAny "." :: Maybe (Either (Path Abs) (Path Rel))--- Nothing+-- Just (Right ".") -- >>> parseAny ".." :: Maybe (Either (Path Abs) (Path Rel)) -- Nothing parseAny :: MonadThrow m => ByteString -> m (Either (Path Abs) (Path Rel))@@ -224,7 +229,10 @@ rootPath :: Path Abs rootPath = (MkPath (BS.singleton _slash)) +pwdPath :: Path Rel+pwdPath = (MkPath (BS.singleton _period)) + -------------------------------------------------------------------------------- -- Path Conversion @@ -262,14 +270,15 @@ -- "/path/to/file" -- >>> (MkPath "/") </> (MkPath "file/lal" :: Path Rel) -- "/file/lal"--- >>> (MkPath "/") </> (MkPath "file" :: Path Rel)+-- >>> (MkPath "/") </> (MkPath "file/" :: Path Rel) -- "/file"+-- >>> (MkPath "/") </> (MkPath "." :: Path Rel)+-- "/"+-- >>> (MkPath ".") </> (MkPath "." :: Path Rel)+-- "." (</>) :: Path b -> Path Rel -> Path b-(</>) (MkPath a) (MkPath b) = MkPath (a' `BS.append` b)- where- a' = if hasTrailingPathSeparator a- then a- else addTrailingPathSeparator a+(</>) (MkPath a) (MkPath b) =+ MkPath (dropTrailingPathSeparator $ normalise (addTrailingPathSeparator a `BS.append` b)) -- | Strip directory from path, making it relative to that directory.@@ -282,18 +291,20 @@ -- >>> (MkPath "lal/lad") `stripDir` (MkPath "lal/lad/fad") :: Maybe (Path Rel) -- Just "fad" -- >>> (MkPath "/") `stripDir` (MkPath "/") :: Maybe (Path Rel)--- Nothing+-- Just "." -- >>> (MkPath "/lal/lad/fad") `stripDir` (MkPath "/lal/lad") :: Maybe (Path Rel) -- Nothing--- >>> (MkPath "fad") `stripDir` (MkPath "fad") :: Maybe (Path Rel)+-- >>> (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 of+ case stripPrefix p' l <|> stripPrefix p l of Nothing -> throwM (Couldn'tStripPrefixTPS p' l) Just ok -> if BS.null ok- then throwM (Couldn'tStripPrefixTPS p' l)+ then return (MkPath $ BS.singleton _period) else return (MkPath ok) where p' = addTrailingPathSeparator p@@ -359,6 +370,8 @@ -- Just "dod" -- >>> basename (MkPath "dod") :: Maybe (Path Rel) -- Just "dod"+-- >>> basename (MkPath ".") :: Maybe (Path Rel)+-- Just "." -- >>> basename (MkPath "/") :: Maybe (Path Rel) -- Nothing basename :: MonadThrow m => Path b -> m (Path Rel)@@ -387,7 +400,11 @@ -- >>> (MkPath "fad") `isParentOf` (MkPath "fad") -- False isParentOf :: Path b -> Path b -> Bool-isParentOf p l = isJust (stripDir p l :: Maybe (Path Rel))+isParentOf p l = case stripDir p l :: Maybe (Path Rel) of+ Nothing -> False+ Just ok+ | isPwdPath ok -> False+ | otherwise -> True -- | Check whether the given Path is the root "/" path.@@ -399,7 +416,16 @@ isRootPath :: Path Abs -> Bool isRootPath = (== rootPath) +-- | Check whether the given Path is the pwd "." path.+--+-- >>> isPwdPath (MkPath "/lal/lad")+-- False+-- >>> isPwdPath (MkPath ".")+-- True+isPwdPath :: Path Rel -> Bool+isPwdPath = (== pwdPath) + -------------------------------------------------------------------------------- -- Path IO helpers @@ -426,8 +452,14 @@ ------------------------ -- QuasiQuoters -instance Lift (Path a) where- lift (MkPath bs) = AppE <$> [| MkPath . BS.pack |] <*> lift (BS.unpack bs)+instance Typeable a => Lift (Path a) where+ lift (MkPath bs) = [| MkPath (BS.pack $(lift $ BS.unpack bs)) :: Path $(pure a) |]+ where+ a = TH.ConT $ TH.Name occ flav+ where+ tc = typeRepTyCon (typeRep (Proxy :: Proxy a))+ occ = TH.OccName (tyConName tc)+ flav = TH.NameG TH.TcClsName (TH.PkgName (tyConPackage tc)) (TH.ModName (tyConModule tc)) qq :: (ByteString -> Q Exp) -> QuasiQuoter