Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28c4f7fe21 | ||
|
|
95fab22567 | ||
|
|
00fabad1f4 | ||
|
|
733fa04ac3 | ||
|
|
339f7587a3 | ||
|
|
0476db8ebc | ||
|
|
57e886b71f | ||
|
|
1841d7451c | ||
|
|
62278a77e6 | ||
|
|
a4ed4cd504 | ||
|
|
c637893aa8 | ||
|
|
09996f1605 | ||
|
|
9f229ecef3 | ||
|
|
44d9e02cc2 | ||
|
|
94a5252ff5 | ||
|
|
df1e191517 | ||
|
|
30890ad3c2 | ||
|
|
9ca83d66b8 | ||
|
|
4e1816392a | ||
|
|
8f3b169760 | ||
|
|
ffdf4af243 | ||
|
|
87a56a93b8 | ||
|
|
cb6f472524 | ||
|
|
bff31277e3 | ||
|
|
b06a8c9528 | ||
|
|
0c1dd7e493 | ||
|
|
5613ba1dfa | ||
|
|
07aa83ca19 | ||
|
|
a68b46b060 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ cabal-dev/
|
|||||||
TAGS
|
TAGS
|
||||||
tags
|
tags
|
||||||
*.tag
|
*.tag
|
||||||
|
.stack-work/
|
||||||
|
|||||||
16
CHANGELOG
16
CHANGELOG
@@ -1,4 +1,20 @@
|
|||||||
|
0.5.3:
|
||||||
|
* Added conversion functions.
|
||||||
|
|
||||||
0.2.0:
|
0.2.0:
|
||||||
|
|
||||||
* Rename parentAbs to simply parent.
|
* Rename parentAbs to simply parent.
|
||||||
* Add dirname.
|
* Add dirname.
|
||||||
|
|
||||||
|
0.3.0:
|
||||||
|
* Removed Generic instance.
|
||||||
|
|
||||||
|
0.4.0:
|
||||||
|
* Implemented stricter parsing, disabling use of "..".
|
||||||
|
* Made stripDir generic over MonadThrow
|
||||||
|
|
||||||
|
0.5.0:
|
||||||
|
* Fix stripDir p p /= Nothing bug.
|
||||||
|
|
||||||
|
0.5.2:
|
||||||
|
* Removed unused DeriveGeneric.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: path
|
name: path
|
||||||
version: 0.2.0
|
version: 0.5.3
|
||||||
synopsis: Path
|
synopsis: Path
|
||||||
description: Path
|
description: Path
|
||||||
license: BSD3
|
license: BSD3
|
||||||
@@ -10,6 +10,7 @@ copyright: 2015 FP Complete
|
|||||||
category: Filesystem
|
category: Filesystem
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
cabal-version: >=1.8
|
cabal-version: >=1.8
|
||||||
|
extra-source-files: README.md, CHANGELOG
|
||||||
|
|
||||||
library
|
library
|
||||||
hs-source-dirs: src/
|
hs-source-dirs: src/
|
||||||
@@ -29,3 +30,7 @@ test-suite test
|
|||||||
, hspec
|
, hspec
|
||||||
, mtl
|
, mtl
|
||||||
, path
|
, path
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://github.com/chrisdone/path.git
|
||||||
|
|||||||
83
src/Path.hs
83
src/Path.hs
@@ -1,5 +1,4 @@
|
|||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE EmptyDataDecls #-}
|
{-# LANGUAGE EmptyDataDecls #-}
|
||||||
|
|
||||||
@@ -32,6 +31,10 @@ module Path
|
|||||||
,dirname
|
,dirname
|
||||||
-- * Conversion
|
-- * Conversion
|
||||||
,toFilePath
|
,toFilePath
|
||||||
|
,fromAbsDir
|
||||||
|
,fromRelDir
|
||||||
|
,fromAbsFile
|
||||||
|
,fromRelFile
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@@ -65,6 +68,7 @@ data PathParseException
|
|||||||
| InvalidRelDir FilePath
|
| InvalidRelDir FilePath
|
||||||
| InvalidAbsFile FilePath
|
| InvalidAbsFile FilePath
|
||||||
| InvalidRelFile FilePath
|
| InvalidRelFile FilePath
|
||||||
|
| Couldn'tStripPrefixDir FilePath FilePath
|
||||||
deriving (Show,Typeable)
|
deriving (Show,Typeable)
|
||||||
instance Exception PathParseException
|
instance Exception PathParseException
|
||||||
|
|
||||||
@@ -81,7 +85,8 @@ parseAbsDir :: MonadThrow m
|
|||||||
parseAbsDir filepath =
|
parseAbsDir filepath =
|
||||||
if FilePath.isAbsolute filepath &&
|
if FilePath.isAbsolute filepath &&
|
||||||
not (null (normalizeDir filepath)) &&
|
not (null (normalizeDir filepath)) &&
|
||||||
not (isPrefixOf "~/" filepath)
|
not ("~/" `isPrefixOf` filepath) &&
|
||||||
|
not (hasParentDir filepath)
|
||||||
then return (Path (normalizeDir filepath))
|
then return (Path (normalizeDir filepath))
|
||||||
else throwM (InvalidAbsDir filepath)
|
else throwM (InvalidAbsDir filepath)
|
||||||
|
|
||||||
@@ -95,8 +100,10 @@ parseRelDir :: MonadThrow m
|
|||||||
parseRelDir filepath =
|
parseRelDir filepath =
|
||||||
if not (FilePath.isAbsolute filepath) &&
|
if not (FilePath.isAbsolute filepath) &&
|
||||||
not (null filepath) &&
|
not (null filepath) &&
|
||||||
not (isPrefixOf "~/" filepath) &&
|
not ("~/" `isPrefixOf` filepath) &&
|
||||||
not (null (normalizeDir filepath))
|
not (hasParentDir filepath) &&
|
||||||
|
not (null (normalizeDir filepath)) &&
|
||||||
|
filepath /= ".."
|
||||||
then return (Path (normalizeDir filepath))
|
then return (Path (normalizeDir filepath))
|
||||||
else throwM (InvalidRelDir filepath)
|
else throwM (InvalidRelDir filepath)
|
||||||
|
|
||||||
@@ -109,8 +116,10 @@ parseAbsFile :: MonadThrow m
|
|||||||
parseAbsFile filepath =
|
parseAbsFile filepath =
|
||||||
if FilePath.isAbsolute filepath &&
|
if FilePath.isAbsolute filepath &&
|
||||||
not (FilePath.hasTrailingPathSeparator filepath) &&
|
not (FilePath.hasTrailingPathSeparator filepath) &&
|
||||||
not (isPrefixOf "~/" filepath) &&
|
not ("~/" `isPrefixOf` filepath) &&
|
||||||
not (null (normalizeFile filepath))
|
not (hasParentDir filepath) &&
|
||||||
|
not (null (normalizeFile filepath)) &&
|
||||||
|
filepath /= ".."
|
||||||
then return (Path (normalizeFile filepath))
|
then return (Path (normalizeFile filepath))
|
||||||
else throwM (InvalidAbsFile filepath)
|
else throwM (InvalidAbsFile filepath)
|
||||||
|
|
||||||
@@ -121,13 +130,29 @@ parseAbsFile filepath =
|
|||||||
parseRelFile :: MonadThrow m
|
parseRelFile :: MonadThrow m
|
||||||
=> FilePath -> m (Path Rel File)
|
=> FilePath -> m (Path Rel File)
|
||||||
parseRelFile filepath =
|
parseRelFile filepath =
|
||||||
if not (FilePath.isAbsolute filepath || FilePath.hasTrailingPathSeparator filepath) &&
|
if not (FilePath.isAbsolute filepath ||
|
||||||
|
FilePath.hasTrailingPathSeparator filepath) &&
|
||||||
not (null filepath) &&
|
not (null filepath) &&
|
||||||
not (isPrefixOf "~/" filepath) &&
|
not ("~/" `isPrefixOf` filepath) &&
|
||||||
not (null (normalizeFile filepath))
|
not (hasParentDir filepath) &&
|
||||||
|
not (null (normalizeFile filepath)) &&
|
||||||
|
filepath /= ".."
|
||||||
then return (Path (normalizeFile filepath))
|
then return (Path (normalizeFile filepath))
|
||||||
else throwM (InvalidRelFile filepath)
|
else throwM (InvalidRelFile filepath)
|
||||||
|
|
||||||
|
-- | Helper function: check if the filepath has any parent directories in it.
|
||||||
|
-- This handles the logic of checking for different path separators on Windows.
|
||||||
|
hasParentDir :: FilePath -> Bool
|
||||||
|
hasParentDir filepath' =
|
||||||
|
("/.." `isSuffixOf` filepath) ||
|
||||||
|
("/../" `isInfixOf` filepath) ||
|
||||||
|
("../" `isPrefixOf` filepath)
|
||||||
|
where
|
||||||
|
filepath =
|
||||||
|
case FilePath.pathSeparator of
|
||||||
|
'/' -> filepath'
|
||||||
|
x -> map (\y -> if x == y then '/' else y) filepath'
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Constructors
|
-- Constructors
|
||||||
|
|
||||||
@@ -175,9 +200,29 @@ mkRelFile s =
|
|||||||
-- Conversion
|
-- Conversion
|
||||||
|
|
||||||
-- | Convert to a 'FilePath' type.
|
-- | Convert to a 'FilePath' type.
|
||||||
|
--
|
||||||
|
-- All directories have a trailing slash, so if you want no trailing
|
||||||
|
-- slash, you can use 'System.FilePath.dropTrailingPathSeparator' from
|
||||||
|
-- the filepath package.
|
||||||
toFilePath :: Path b t -> FilePath
|
toFilePath :: Path b t -> FilePath
|
||||||
toFilePath (Path l) = l
|
toFilePath (Path l) = l
|
||||||
|
|
||||||
|
-- | Convert absolute path to directory to 'FilePath' type.
|
||||||
|
fromAbsDir :: Path Abs Dir -> FilePath
|
||||||
|
fromAbsDir = toFilePath
|
||||||
|
|
||||||
|
-- | Convert relative path to directory to 'FilePath' type.
|
||||||
|
fromRelDir :: Path Rel Dir -> FilePath
|
||||||
|
fromRelDir = toFilePath
|
||||||
|
|
||||||
|
-- | Convert absolute path to file to 'FilePath' type.
|
||||||
|
fromAbsFile :: Path Abs File -> FilePath
|
||||||
|
fromAbsFile = toFilePath
|
||||||
|
|
||||||
|
-- | Convert relative path to file to 'FilePath' type.
|
||||||
|
fromRelFile :: Path Rel File -> FilePath
|
||||||
|
fromRelFile = toFilePath
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Operations
|
-- Operations
|
||||||
|
|
||||||
@@ -211,19 +256,25 @@ toFilePath (Path l) = l
|
|||||||
--
|
--
|
||||||
-- The following properties hold:
|
-- The following properties hold:
|
||||||
--
|
--
|
||||||
-- @stripDir parent (parent \<\/> child) = child@
|
-- @stripDir x (x \<\/> y) = y@
|
||||||
--
|
--
|
||||||
-- Cases which are proven not possible:
|
-- Cases which are proven not possible:
|
||||||
--
|
--
|
||||||
-- @stripDir (a :: Path Abs …) (b :: Path Rel …)@
|
-- @stripDir (a :: Path Abs …) (b :: Path Rel …)@
|
||||||
--
|
--
|
||||||
-- @stripDir (a :: Path Rel) (b :: Path Abs …)@
|
-- @stripDir (a :: Path Rel …) (b :: Path Abs …)@
|
||||||
--
|
--
|
||||||
-- In other words the bases must match.
|
-- In other words the bases must match.
|
||||||
--
|
--
|
||||||
stripDir :: Path b Dir -> Path b t -> Maybe (Path Rel t)
|
-- Throws: 'Couldn'tStripPrefixDir'
|
||||||
|
--
|
||||||
|
stripDir :: MonadThrow m
|
||||||
|
=> Path b Dir -> Path b t -> m (Path Rel t)
|
||||||
stripDir (Path p) (Path l) =
|
stripDir (Path p) (Path l) =
|
||||||
fmap Path (stripPrefix p l)
|
case stripPrefix p l of
|
||||||
|
Nothing -> throwM (Couldn'tStripPrefixDir p l)
|
||||||
|
Just "" -> throwM (Couldn'tStripPrefixDir p l)
|
||||||
|
Just ok -> return (Path ok)
|
||||||
|
|
||||||
-- | Is p a parent of the given location? Implemented in terms of
|
-- | Is p a parent of the given location? Implemented in terms of
|
||||||
-- 'stripDir'. The bases must match.
|
-- 'stripDir'. The bases must match.
|
||||||
@@ -235,7 +286,7 @@ isParentOf p l =
|
|||||||
--
|
--
|
||||||
-- The following properties hold:
|
-- The following properties hold:
|
||||||
--
|
--
|
||||||
-- @parent (parent \<\/> child) == parent@
|
-- @parent (x \<\/> y) == x@
|
||||||
--
|
--
|
||||||
-- On the root, getting the parent is idempotent:
|
-- On the root, getting the parent is idempotent:
|
||||||
--
|
--
|
||||||
@@ -249,7 +300,7 @@ parent (Path fp) =
|
|||||||
--
|
--
|
||||||
-- The following properties hold:
|
-- The following properties hold:
|
||||||
--
|
--
|
||||||
-- @filename (parent \<\/> filename a) == a@
|
-- @filename (p \<\/> a) == filename a@
|
||||||
--
|
--
|
||||||
filename :: Path b File -> Path Rel File
|
filename :: Path b File -> Path Rel File
|
||||||
filename (Path l) =
|
filename (Path l) =
|
||||||
@@ -259,7 +310,7 @@ filename (Path l) =
|
|||||||
--
|
--
|
||||||
-- The following properties hold:
|
-- The following properties hold:
|
||||||
--
|
--
|
||||||
-- @dirname (parent \<\/> dirname a) == a@
|
-- @dirname (p \<\/> a) == dirname a@
|
||||||
--
|
--
|
||||||
dirname :: Path b Dir -> Path Rel Dir
|
dirname :: Path b Dir -> Path Rel Dir
|
||||||
dirname (Path l) =
|
dirname (Path l) =
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
{-# LANGUAGE StandaloneDeriving #-}
|
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
||||||
|
|
||||||
-- | Internal types and functions.
|
-- | Internal types and functions.
|
||||||
|
|
||||||
@@ -10,7 +7,6 @@ module Path.Internal
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Data.Data
|
import Data.Data
|
||||||
import GHC.Generics
|
|
||||||
|
|
||||||
-- | Path of some base and type.
|
-- | Path of some base and type.
|
||||||
--
|
--
|
||||||
@@ -22,7 +18,7 @@ import GHC.Generics
|
|||||||
-- All directories end in a trailing separator. There are no duplicate
|
-- All directories end in a trailing separator. There are no duplicate
|
||||||
-- path separators @\/\/@, no @..@, no @.\/@, no @~\/@, etc.
|
-- path separators @\/\/@, no @..@, no @.\/@, no @~\/@, etc.
|
||||||
newtype Path b t = Path FilePath
|
newtype Path b t = Path FilePath
|
||||||
deriving (Typeable,Generic)
|
deriving (Typeable)
|
||||||
|
|
||||||
-- | String equality.
|
-- | String equality.
|
||||||
--
|
--
|
||||||
|
|||||||
36
test/Main.hs
36
test/Main.hs
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import Path
|
import Path
|
||||||
import Path.Internal
|
import Path.Internal
|
||||||
@@ -23,8 +26,27 @@ spec =
|
|||||||
describe "Operations: (</>)" operationAppend
|
describe "Operations: (</>)" operationAppend
|
||||||
describe "Operations: stripDir" operationStripDir
|
describe "Operations: stripDir" operationStripDir
|
||||||
describe "Operations: isParentOf" operationIsParentOf
|
describe "Operations: isParentOf" operationIsParentOf
|
||||||
describe "Operations: parentAbs" operationParentAbs
|
describe "Operations: parent" operationParent
|
||||||
describe "Operations: filename" operationFilename
|
describe "Operations: filename" operationFilename
|
||||||
|
describe "Restrictions" restrictions
|
||||||
|
|
||||||
|
-- | Restricting the input of any tricks.
|
||||||
|
restrictions :: Spec
|
||||||
|
restrictions =
|
||||||
|
do parseFails "~/"
|
||||||
|
parseFails "~/foo"
|
||||||
|
parseFails "~/foo/bar"
|
||||||
|
parseFails "../"
|
||||||
|
parseFails ".."
|
||||||
|
parseFails "/.."
|
||||||
|
parseFails "/foo/../bar/"
|
||||||
|
parseFails "/foo/bar/.."
|
||||||
|
where parseFails x =
|
||||||
|
it (show x ++ " should be rejected")
|
||||||
|
(isNothing (void (parseAbsDir x) <|>
|
||||||
|
void (parseRelDir x) <|>
|
||||||
|
void (parseAbsFile x) <|>
|
||||||
|
void (parseRelFile x)))
|
||||||
|
|
||||||
-- | The 'filename' operation.
|
-- | The 'filename' operation.
|
||||||
operationFilename :: Spec
|
operationFilename :: Spec
|
||||||
@@ -38,10 +60,10 @@ operationFilename =
|
|||||||
filename $(mkRelFile "bar.txt")) ==
|
filename $(mkRelFile "bar.txt")) ==
|
||||||
$(mkRelFile "bar.txt"))
|
$(mkRelFile "bar.txt"))
|
||||||
|
|
||||||
-- | The 'parentAbs' operation.
|
-- | The 'parent' operation.
|
||||||
operationParentAbs :: Spec
|
operationParent :: Spec
|
||||||
operationParentAbs =
|
operationParent =
|
||||||
do it "parentAbs (parent </> child) == parent"
|
do it "parent (parent </> child) == parent"
|
||||||
(parent ($(mkAbsDir "/foo") </>
|
(parent ($(mkAbsDir "/foo") </>
|
||||||
$(mkRelDir "bar")) ==
|
$(mkRelDir "bar")) ==
|
||||||
$(mkAbsDir "/foo"))
|
$(mkAbsDir "/foo"))
|
||||||
@@ -79,6 +101,10 @@ operationStripDir =
|
|||||||
($(mkRelDir "bar/") </>
|
($(mkRelDir "bar/") </>
|
||||||
$(mkRelFile "bob/foo.txt")) ==
|
$(mkRelFile "bob/foo.txt")) ==
|
||||||
Just $(mkRelFile "bob/foo.txt"))
|
Just $(mkRelFile "bob/foo.txt"))
|
||||||
|
it "stripDir parent parent = _|_"
|
||||||
|
(stripDir $(mkAbsDir "/home/chris/foo")
|
||||||
|
$(mkAbsDir "/home/chris/foo") ==
|
||||||
|
Nothing)
|
||||||
|
|
||||||
-- | The '</>' operation.
|
-- | The '</>' operation.
|
||||||
operationAppend :: Spec
|
operationAppend :: Spec
|
||||||
|
|||||||
Reference in New Issue
Block a user