{-# LANGUAGE OverloadedStrings, BangPatterns #-}

module Network.Wai.Application.Classic.Path (
    Path
  , pathString
  , fromString
  , (</>), (<\>), (<.>)
  , breakAtSeparator, hasLeadingPathSeparator, hasTrailingPathSeparator
  , isSuffixOf
  ) where

import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as B8
import Data.String
import Data.Word

----------------------------------------------------------------

-- | File path.
type Path = ByteString

pathString :: Path -> String
pathString :: Path -> String
pathString = Path -> String
B8.unpack
{-# INLINE pathString #-}

----------------------------------------------------------------

-- pathDot :: Word8
-- pathDot = 46

pathDotBS :: ByteString
pathDotBS :: Path
pathDotBS = Path
"."

pathSep :: Word8
pathSep :: Word8
pathSep = Word8
47

pathSepBS :: ByteString
pathSepBS :: Path
pathSepBS = Path
"/"

{-|
  Checking if the path ends with the path separator.

>>> hasLeadingPathSeparator "/foo/bar"
True
>>> hasLeadingPathSeparator "foo/bar"
False
-}
hasLeadingPathSeparator :: Path -> Bool
hasLeadingPathSeparator :: Path -> Bool
hasLeadingPathSeparator Path
bs
  | Path -> Bool
BS.null Path
bs            = Bool
False
  | Path -> Word8
BS.head Path
bs Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
pathSep = Bool
True
  | Bool
otherwise             = Bool
False
{-# INLINE hasLeadingPathSeparator #-}

{-|
  Checking if the path ends with the path separator.

>>> hasTrailingPathSeparator "/foo/bar/"
True
>>> hasTrailingPathSeparator "/foo/bar"
False
-}
hasTrailingPathSeparator :: Path -> Bool
hasTrailingPathSeparator :: Path -> Bool
hasTrailingPathSeparator Path
bs
  | Path -> Bool
BS.null Path
bs            = Bool
False
  | Path -> Word8
BS.last Path
bs Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
pathSep = Bool
True
  | Bool
otherwise             = Bool
False
{-# INLINE hasTrailingPathSeparator #-}

{-|
  Appending with the file separator.

>>> "/foo" </> "bar"
"/foo/bar"
>>> "/foo/" </> "bar"
"/foo/bar"
>>> "/foo" </> "/bar"
"/foo/bar"
>>> "/foo/" </> "/bar"
"/foo/bar"
-}

(</>) :: Path -> Path -> Path
Path
p1 </> :: Path -> Path -> Path
</> Path
p2 = Path
p
  where
    !has1 :: Bool
has1 = Path -> Bool
hasTrailingPathSeparator Path
p1
    !has2 :: Bool
has2 = Path -> Bool
hasLeadingPathSeparator Path
p2
    !p :: Path
p | Bool
has1 Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
has2 = Path
p1 Path -> Path -> Path
`BS.append` Path
p2
       | Bool -> Bool
not Bool
has1 Bool -> Bool -> Bool
&& Bool
has2 = Path
p1 Path -> Path -> Path
`BS.append` Path
p2
       | Bool
has1             = Path
p1 Path -> Path -> Path
`BS.append` Path -> Path
BS.tail Path
p2
       | Bool
otherwise        = [Path] -> Path
BS.concat [Path
p1,Path
pathSepBS,Path
p2]
{-# INLINE (</>) #-}

{-|
  Removing prefix. The prefix of the second argument is removed
  from the first argument.

>>> "foobar" <\> "foo"
"bar"
>>> "foo" <\> "foobar"
""
>>> "foobar" <\> "baz"
"bar"
-}
(<\>) :: Path -> Path -> Path
Path
p1 <\> :: Path -> Path -> Path
<\> Path
p2 = Path
p
  where
    !p :: Path
p = Int -> Path -> Path
BS.drop (Path -> Int
BS.length Path
p2) Path
p1
{-# INLINE (<\>) #-}

{-|
  Adding suffix.
-}
(<.>) :: Path -> Path -> Path
Path
p1 <.> :: Path -> Path -> Path
<.> Path
p2 = Path
p
  where
    !p :: Path
p = [Path] -> Path
BS.concat [Path
p1,Path
pathDotBS,Path
p2]
{-# INLINE (<.>) #-}

{-|
  Breaking at the first path separator.

>>> breakAtSeparator "/foo/bar/baz"
("","/foo/bar/baz")
>>> breakAtSeparator "foo/bar/baz"
("foo","/bar/baz")
>>> breakAtSeparator "foo"
("foo","")
-}
breakAtSeparator :: Path -> (Path,Path)
breakAtSeparator :: Path -> (Path, Path)
breakAtSeparator Path
p = (Word8 -> Bool) -> Path -> (Path, Path)
BS.break (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
pathSep) Path
p
{-# INLINE breakAtSeparator #-}

isSuffixOf :: Path -> Path -> Bool
isSuffixOf :: Path -> Path -> Bool
isSuffixOf = Path -> Path -> Bool
BS.isSuffixOf
{-# INLINE isSuffixOf #-}