    {-# 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

    -- $setup
    -- >>> :set -XOverloadedStrings

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

    -- | File path.
    type Path = ByteString

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

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

    -- pathDot :: Word8
    -- pathDot = 46

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

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

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

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

    >>> hasLeadingPathSeparator "/foo/bar"
    True
    >>> hasLeadingPathSeparator "foo/bar"
    False
    -}
    hasLeadingPathSeparator :: Path -> Bool
    hasLeadingPathSeparator :: ByteString -> Bool
    hasLeadingPathSeparator ByteString
    bs
      | ByteString -> Bool
    BS.null ByteString
    bs            = Bool
    False
      | HasCallStack => ByteString -> Word8
    ByteString -> Word8
    BS.head ByteString
    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 :: ByteString -> Bool
    hasTrailingPathSeparator ByteString
    bs
      | ByteString -> Bool
    BS.null ByteString
    bs            = Bool
    False
      | HasCallStack => ByteString -> Word8
    ByteString -> Word8
    BS.last ByteString
    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
    ByteString
    p1 </> :: ByteString -> ByteString -> ByteString
    </> ByteString
    p2 = ByteString
    p
      where
        !has1 :: Bool
    has1 = ByteString -> Bool
    hasTrailingPathSeparator ByteString
    p1
        !has2 :: Bool
    has2 = ByteString -> Bool
    hasLeadingPathSeparator ByteString
    p2
        !p :: ByteString
    p | Bool
    has1 Bool -> Bool -> Bool
    && Bool -> Bool
    not Bool
    has2 = ByteString
    p1 ByteString -> ByteString -> ByteString
    `BS.append` ByteString
    p2
           | Bool -> Bool
    not Bool
    has1 Bool -> Bool -> Bool
    && Bool
    has2 = ByteString
    p1 ByteString -> ByteString -> ByteString
    `BS.append` ByteString
    p2
           | Bool
    has1             = ByteString
    p1 ByteString -> ByteString -> ByteString
    `BS.append` HasCallStack => ByteString -> ByteString
    ByteString -> ByteString
    BS.tail ByteString
    p2
           | Bool
    otherwise        = [ByteString] -> ByteString
    BS.concat [ByteString
    p1,ByteString
    pathSepBS,ByteString
    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
    ByteString
    p1 <\> :: ByteString -> ByteString -> ByteString
    <\> ByteString
    p2 = ByteString
    p
      where
        !p :: ByteString
    p = Int -> ByteString -> ByteString
    BS.drop (ByteString -> Int
    BS.length ByteString
    p2) ByteString
    p1
    {-# INLINE (<\>) #-}

    {-|
      Adding suffix.
    -}
    (<.>) :: Path -> Path -> Path
    ByteString
    p1 <.> :: ByteString -> ByteString -> ByteString
    <.> ByteString
    p2 = ByteString
    p
      where
        !p :: ByteString
    p = [ByteString] -> ByteString
    BS.concat [ByteString
    p1,ByteString
    pathDotBS,ByteString
    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 :: ByteString -> (ByteString, ByteString)
    breakAtSeparator ByteString
    p = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
    BS.break (Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    pathSep) ByteString
    p
    {-# INLINE breakAtSeparator #-}

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