{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
#if __GLASGOW_HASKELL__ >= 708
{-# LANGUAGE PatternSynonyms #-}
#endif
module HPath
(
Abs
,Path
,Rel
,Fn
,PathParseException
,PathException
,RelC
#if __GLASGOW_HASKELL__ >= 708
,pattern Path
#endif
,parseAbs
,parseFn
,parseRel
,fromAbs
,fromRel
,toFilePath
,(</>)
,basename
,dirname
,isParentOf
,getAllParents
,stripDir
,withAbsPath
,withRelPath
,withFnPath
)
where
import Control.Exception (Exception)
import Control.Monad.Catch (MonadThrow(..))
#if MIN_VERSION_bytestring(0,10,8)
import Data.ByteString(ByteString, stripPrefix)
#else
import Data.ByteString(ByteString)
import qualified Data.List as L
#endif
import qualified Data.ByteString as BS
import Data.Data
import Data.Maybe
import Data.Word8
import HPath.Internal
import System.Posix.FilePath hiding ((</>))
data Abs deriving (Typeable)
data Rel deriving (Typeable)
data Fn deriving (Typeable)
data PathParseException
= InvalidAbs ByteString
| InvalidRel ByteString
| InvalidFn ByteString
| Couldn'tStripPrefixTPS ByteString ByteString
deriving (Int -> PathParseException -> ShowS
[PathParseException] -> ShowS
PathParseException -> String
(Int -> PathParseException -> ShowS)
-> (PathParseException -> String)
-> ([PathParseException] -> ShowS)
-> Show PathParseException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PathParseException] -> ShowS
$cshowList :: [PathParseException] -> ShowS
show :: PathParseException -> String
$cshow :: PathParseException -> String
showsPrec :: Int -> PathParseException -> ShowS
$cshowsPrec :: Int -> PathParseException -> ShowS
Show,Typeable)
instance Exception PathParseException
data PathException = RootDirHasNoBasename
deriving (Int -> PathException -> ShowS
[PathException] -> ShowS
PathException -> String
(Int -> PathException -> ShowS)
-> (PathException -> String)
-> ([PathException] -> ShowS)
-> Show PathException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PathException] -> ShowS
$cshowList :: [PathException] -> ShowS
show :: PathException -> String
$cshow :: PathException -> String
showsPrec :: Int -> PathException -> ShowS
$cshowsPrec :: Int -> PathException -> ShowS
Show,Typeable)
instance Exception PathException
class RelC m
instance RelC Rel
instance RelC Fn
#if __GLASGOW_HASKELL__ >= 710
pattern Path :: ByteString -> Path a
#endif
#if __GLASGOW_HASKELL__ >= 708
pattern $mPath :: forall r a. Path a -> (ByteString -> r) -> (Void# -> r) -> r
Path x <- (MkPath x)
#endif
parseAbs :: MonadThrow m
=> ByteString -> m (Path Abs)
parseAbs :: ByteString -> m (Path Abs)
parseAbs filepath :: ByteString
filepath =
if ByteString -> Bool
isAbsolute ByteString
filepath Bool -> Bool -> Bool
&&
ByteString -> Bool
isValid ByteString
filepath Bool -> Bool -> Bool
&&
Bool -> Bool
not (ByteString -> Bool
hasParentDir ByteString
filepath)
then Path Abs -> m (Path Abs)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Path Abs
forall b. ByteString -> Path b
MkPath (ByteString -> Path Abs) -> ByteString -> Path Abs
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
normalise ByteString
filepath)
else PathParseException -> m (Path Abs)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ByteString -> PathParseException
InvalidAbs ByteString
filepath)
parseRel :: MonadThrow m
=> ByteString -> m (Path Rel)
parseRel :: ByteString -> m (Path Rel)
parseRel filepath :: ByteString
filepath =
if Bool -> Bool
not (ByteString -> Bool
isAbsolute ByteString
filepath) Bool -> Bool -> Bool
&&
ByteString
filepath ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8 -> ByteString
BS.singleton Word8
_period Bool -> Bool -> Bool
&&
ByteString
filepath ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= [Word8] -> ByteString
BS.pack [Word8
_period, Word8
_period] Bool -> Bool -> Bool
&&
Bool -> Bool
not (ByteString -> Bool
hasParentDir ByteString
filepath) Bool -> Bool -> Bool
&&
ByteString -> Bool
isValid ByteString
filepath
then Path Rel -> m (Path Rel)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Path Rel
forall b. ByteString -> Path b
MkPath (ByteString -> Path Rel) -> ByteString -> Path Rel
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
normalise ByteString
filepath)
else PathParseException -> m (Path Rel)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ByteString -> PathParseException
InvalidRel ByteString
filepath)
parseFn :: MonadThrow m
=> ByteString -> m (Path Fn)
parseFn :: ByteString -> m (Path Fn)
parseFn filepath :: ByteString
filepath =
if ByteString -> Bool
isFileName ByteString
filepath Bool -> Bool -> Bool
&&
ByteString
filepath ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8 -> ByteString
BS.singleton Word8
_period Bool -> Bool -> Bool
&&
ByteString
filepath ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= [Word8] -> ByteString
BS.pack [Word8
_period, Word8
_period] Bool -> Bool -> Bool
&&
ByteString -> Bool
isValid ByteString
filepath
then Path Fn -> m (Path Fn)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Path Fn
forall b. ByteString -> Path b
MkPath ByteString
filepath)
else PathParseException -> m (Path Fn)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ByteString -> PathParseException
InvalidFn ByteString
filepath)
toFilePath :: Path b -> ByteString
toFilePath :: Path b -> ByteString
toFilePath (MkPath l :: ByteString
l) = ByteString
l
fromAbs :: Path Abs -> ByteString
fromAbs :: Path Abs -> ByteString
fromAbs = Path Abs -> ByteString
forall b. Path b -> ByteString
toFilePath
fromRel :: RelC r => Path r -> ByteString
fromRel :: Path r -> ByteString
fromRel = Path r -> ByteString
forall b. Path b -> ByteString
toFilePath
(</>) :: RelC r => Path b -> Path r -> Path b
</> :: Path b -> Path r -> Path b
(</>) (MkPath a :: ByteString
a) (MkPath b :: ByteString
b) = ByteString -> Path b
forall b. ByteString -> Path b
MkPath (ByteString
a' ByteString -> ByteString -> ByteString
`BS.append` ByteString
b)
where
a' :: ByteString
a' = if ByteString -> Word8
BS.last ByteString
a Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
pathSeparator
then ByteString
a
else ByteString -> ByteString
addTrailingPathSeparator ByteString
a
stripDir :: MonadThrow m
=> Path b -> Path b -> m (Path Rel)
stripDir :: Path b -> Path b -> m (Path Rel)
stripDir (MkPath p :: ByteString
p) (MkPath l :: ByteString
l) =
case ByteString -> ByteString -> Maybe ByteString
stripPrefix ByteString
p' ByteString
l of
Nothing -> PathParseException -> m (Path Rel)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ByteString -> ByteString -> PathParseException
Couldn'tStripPrefixTPS ByteString
p' ByteString
l)
Just ok :: ByteString
ok -> if ByteString -> Bool
BS.null ByteString
ok
then PathParseException -> m (Path Rel)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ByteString -> ByteString -> PathParseException
Couldn'tStripPrefixTPS ByteString
p' ByteString
l)
else Path Rel -> m (Path Rel)
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Path Rel
forall b. ByteString -> Path b
MkPath ByteString
ok)
where
p' :: ByteString
p' = ByteString -> ByteString
addTrailingPathSeparator ByteString
p
isParentOf :: Path b -> Path b -> Bool
isParentOf :: Path b -> Path b -> Bool
isParentOf p :: Path b
p l :: Path b
l = Maybe (Path Rel) -> Bool
forall a. Maybe a -> Bool
isJust (Path b -> Path b -> Maybe (Path Rel)
forall (m :: * -> *) b.
MonadThrow m =>
Path b -> Path b -> m (Path Rel)
stripDir Path b
p Path b
l :: Maybe (Path Rel))
getAllParents :: Path Abs -> [Path Abs]
getAllParents :: Path Abs -> [Path Abs]
getAllParents (MkPath p :: ByteString
p)
| ByteString
np ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== Word8 -> ByteString
BS.singleton Word8
pathSeparator = []
| Bool
otherwise = Path Abs -> Path Abs
dirname (ByteString -> Path Abs
forall b. ByteString -> Path b
MkPath ByteString
np) Path Abs -> [Path Abs] -> [Path Abs]
forall a. a -> [a] -> [a]
: Path Abs -> [Path Abs]
getAllParents (Path Abs -> Path Abs
dirname (Path Abs -> Path Abs) -> Path Abs -> Path Abs
forall a b. (a -> b) -> a -> b
$ ByteString -> Path Abs
forall b. ByteString -> Path b
MkPath ByteString
np)
where
np :: ByteString
np = ByteString -> ByteString
dropTrailingPathSeparator (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
normalise (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
p
dirname :: Path Abs -> Path Abs
dirname :: Path Abs -> Path Abs
dirname (MkPath fp :: ByteString
fp) = ByteString -> Path Abs
forall b. ByteString -> Path b
MkPath (ByteString -> ByteString
takeDirectory (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
dropTrailingPathSeparator ByteString
fp)
basename :: MonadThrow m => Path b -> m (Path Fn)
basename :: Path b -> m (Path Fn)
basename (MkPath l :: ByteString
l)
| Bool -> Bool
not (ByteString -> Bool
isAbsolute ByteString
rl) = Path Fn -> m (Path Fn)
forall (m :: * -> *) a. Monad m => a -> m a
return (Path Fn -> m (Path Fn)) -> Path Fn -> m (Path Fn)
forall a b. (a -> b) -> a -> b
$ ByteString -> Path Fn
forall b. ByteString -> Path b
MkPath ByteString
rl
| Bool
otherwise = PathException -> m (Path Fn)
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM PathException
RootDirHasNoBasename
where
rl :: ByteString
rl = [ByteString] -> ByteString
forall a. [a] -> a
last ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
splitPath (ByteString -> [ByteString])
-> (ByteString -> ByteString) -> ByteString -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
dropTrailingPathSeparator (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString
l
withAbsPath :: Path Abs -> (ByteString -> IO a) -> IO a
withAbsPath :: Path Abs -> (ByteString -> IO a) -> IO a
withAbsPath (MkPath p :: ByteString
p) action :: ByteString -> IO a
action = ByteString -> IO a
action ByteString
p
withRelPath :: Path Rel -> (ByteString -> IO a) -> IO a
withRelPath :: Path Rel -> (ByteString -> IO a) -> IO a
withRelPath (MkPath p :: ByteString
p) action :: ByteString -> IO a
action = ByteString -> IO a
action ByteString
p
withFnPath :: Path Fn -> (ByteString -> IO a) -> IO a
withFnPath :: Path Fn -> (ByteString -> IO a) -> IO a
withFnPath (MkPath p :: ByteString
p) action :: ByteString -> IO a
action = ByteString -> IO a
action ByteString
p
#if MIN_VERSION_bytestring(0,10,8)
#else
stripPrefix :: ByteString -> ByteString -> Maybe ByteString
stripPrefix a b = BS.pack `fmap` L.stripPrefix (BS.unpack a) (BS.unpack b)
#endif