    {-# LANGUAGE CPP #-}
    {-# LANGUAGE DeriveDataTypeable #-}

    -- |
    -- Module: Filesystem.Path.Internal
    -- Copyright: 2010 John Millikin
    -- License: MIT
    --
    -- Maintainer:  jmillikin@gmail.com
    -- Portability:  portable
    --
    module Filesystem.Path.Internal where

    import           Prelude hiding (FilePath)

    import           Control.DeepSeq (NFData, rnf)
    import qualified Control.Exception as Exc
    import qualified Data.ByteString as B
    import qualified Data.ByteString.Char8 as B8
    import           Data.Char (chr, ord)
    import           Data.Data (Data)
    import           Data.List (intersperse)
    import           Data.Ord (comparing)
    import qualified Data.Text as T
    import qualified Data.Text.Encoding as TE
    import           Data.Text.Encoding.Error (UnicodeException)
    import           Data.Typeable (Typeable)

    -------------------------------------------------------------------------------
    -- File Paths
    -------------------------------------------------------------------------------

    type Chunk = String
    type Directory = Chunk
    type Basename = Chunk
    type Extension = Chunk

    data Root
      = RootPosix
      | RootWindowsVolume Char Bool
      | RootWindowsCurrentVolume
      | RootWindowsUnc String String Bool
      | RootWindowsDoubleQMark
      deriving (Root -> Root -> Bool
    (Root -> Root -> Bool) -> (Root -> Root -> Bool) -> Eq Root
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Root -> Root -> Bool
    == :: Root -> Root -> Bool
    $c/= :: Root -> Root -> Bool
    /= :: Root -> Root -> Bool
    Eq, Eq Root
    Eq Root =>
    (Root -> Root -> Ordering)
    -> (Root -> Root -> Bool)
    -> (Root -> Root -> Bool)
    -> (Root -> Root -> Bool)
    -> (Root -> Root -> Bool)
    -> (Root -> Root -> Root)
    -> (Root -> Root -> Root)
    -> Ord Root
    Root -> Root -> Bool
    Root -> Root -> Ordering
    Root -> Root -> Root
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: Root -> Root -> Ordering
    compare :: Root -> Root -> Ordering
    $c< :: Root -> Root -> Bool
    < :: Root -> Root -> Bool
    $c<= :: Root -> Root -> Bool
    <= :: Root -> Root -> Bool
    $c> :: Root -> Root -> Bool
    > :: Root -> Root -> Bool
    $c>= :: Root -> Root -> Bool
    >= :: Root -> Root -> Bool
    $cmax :: Root -> Root -> Root
    max :: Root -> Root -> Root
    $cmin :: Root -> Root -> Root
    min :: Root -> Root -> Root
    Ord, Typeable Root
    Typeable Root =>
    (forall (c :: * -> *).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> Root -> c Root)
    -> (forall (c :: * -> *).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c Root)
    -> (Root -> Constr)
    -> (Root -> DataType)
    -> (forall (t :: * -> *) (c :: * -> *).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c Root))
    -> (forall (t :: * -> * -> *) (c :: * -> *).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Root))
    -> ((forall b. Data b => b -> b) -> Root -> Root)
    -> (forall r r'.
        (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r)
    -> (forall r r'.
        (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r)
    -> (forall u. (forall d. Data d => d -> u) -> Root -> [u])
    -> (forall u. Int -> (forall d. Data d => d -> u) -> Root -> u)
    -> (forall (m :: * -> *).
        Monad m =>
        (forall d. Data d => d -> m d) -> Root -> m Root)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> Root -> m Root)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> Root -> m Root)
    -> Data Root
    Root -> Constr
    Root -> DataType
    (forall b. Data b => b -> b) -> Root -> Root
    forall a.
    Typeable a =>
    (forall (c :: * -> *).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> a -> c a)
    -> (forall (c :: * -> *).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c a)
    -> (a -> Constr)
    -> (a -> DataType)
    -> (forall (t :: * -> *) (c :: * -> *).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c a))
    -> (forall (t :: * -> * -> *) (c :: * -> *).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
    -> ((forall b. Data b => b -> b) -> a -> a)
    -> (forall r r'.
        (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall r r'.
        (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall u. (forall d. Data d => d -> u) -> a -> [u])
    -> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
    -> (forall (m :: * -> *).
        Monad m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> Data a
    forall u. Int -> (forall d. Data d => d -> u) -> Root -> u
    forall u. (forall d. Data d => d -> u) -> Root -> [u]
    forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Root
    forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Root -> c Root
    forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Root)
    forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Root)
    $cgfoldl :: forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Root -> c Root
    gfoldl :: forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Root -> c Root
    $cgunfold :: forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Root
    gunfold :: forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Root
    $ctoConstr :: Root -> Constr
    toConstr :: Root -> Constr
    $cdataTypeOf :: Root -> DataType
    dataTypeOf :: Root -> DataType
    $cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Root)
    dataCast1 :: forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Root)
    $cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Root)
    dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Root)
    $cgmapT :: (forall b. Data b => b -> b) -> Root -> Root
    gmapT :: (forall b. Data b => b -> b) -> Root -> Root
    $cgmapQl :: forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    gmapQl :: forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    $cgmapQr :: forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    gmapQr :: forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Root -> r
    $cgmapQ :: forall u. (forall d. Data d => d -> u) -> Root -> [u]
    gmapQ :: forall u. (forall d. Data d => d -> u) -> Root -> [u]
    $cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Root -> u
    gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Root -> u
    $cgmapM :: forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    gmapM :: forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    $cgmapMp :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    gmapMp :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    $cgmapMo :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    gmapMo :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Root -> m Root
    Data, Typeable, Int -> Root -> ShowS
    [Root] -> ShowS
    Root -> String
    (Int -> Root -> ShowS)
    -> (Root -> String) -> ([Root] -> ShowS) -> Show Root
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Root -> ShowS
    showsPrec :: Int -> Root -> ShowS
    $cshow :: Root -> String
    show :: Root -> String
    $cshowList :: [Root] -> ShowS
    showList :: [Root] -> ShowS
    Show)

    data FilePath = FilePath
      { FilePath -> Maybe Root
    pathRoot :: Maybe Root
      , FilePath -> [String]
    pathDirectories :: [Directory]
      , FilePath -> Maybe String
    pathBasename :: Maybe Basename
      , FilePath -> [String]
    pathExtensions :: [Extension]
      }
      deriving (Typeable FilePath
    Typeable FilePath =>
    (forall (c :: * -> *).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> FilePath -> c FilePath)
    -> (forall (c :: * -> *).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c FilePath)
    -> (FilePath -> Constr)
    -> (FilePath -> DataType)
    -> (forall (t :: * -> *) (c :: * -> *).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c FilePath))
    -> (forall (t :: * -> * -> *) (c :: * -> *).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c FilePath))
    -> ((forall b. Data b => b -> b) -> FilePath -> FilePath)
    -> (forall r r'.
        (r -> r' -> r)
        -> r -> (forall d. Data d => d -> r') -> FilePath -> r)
    -> (forall r r'.
        (r' -> r -> r)
        -> r -> (forall d. Data d => d -> r') -> FilePath -> r)
    -> (forall u. (forall d. Data d => d -> u) -> FilePath -> [u])
    -> (forall u. Int -> (forall d. Data d => d -> u) -> FilePath -> u)
    -> (forall (m :: * -> *).
        Monad m =>
        (forall d. Data d => d -> m d) -> FilePath -> m FilePath)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> FilePath -> m FilePath)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> FilePath -> m FilePath)
    -> Data FilePath
    FilePath -> Constr
    FilePath -> DataType
    (forall b. Data b => b -> b) -> FilePath -> FilePath
    forall a.
    Typeable a =>
    (forall (c :: * -> *).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> a -> c a)
    -> (forall (c :: * -> *).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c a)
    -> (a -> Constr)
    -> (a -> DataType)
    -> (forall (t :: * -> *) (c :: * -> *).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c a))
    -> (forall (t :: * -> * -> *) (c :: * -> *).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
    -> ((forall b. Data b => b -> b) -> a -> a)
    -> (forall r r'.
        (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall r r'.
        (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall u. (forall d. Data d => d -> u) -> a -> [u])
    -> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
    -> (forall (m :: * -> *).
        Monad m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: * -> *).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> Data a
    forall u. Int -> (forall d. Data d => d -> u) -> FilePath -> u
    forall u. (forall d. Data d => d -> u) -> FilePath -> [u]
    forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c FilePath
    forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> FilePath -> c FilePath
    forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c FilePath)
    forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c FilePath)
    $cgfoldl :: forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> FilePath -> c FilePath
    gfoldl :: forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> FilePath -> c FilePath
    $cgunfold :: forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c FilePath
    gunfold :: forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c FilePath
    $ctoConstr :: FilePath -> Constr
    toConstr :: FilePath -> Constr
    $cdataTypeOf :: FilePath -> DataType
    dataTypeOf :: FilePath -> DataType
    $cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c FilePath)
    dataCast1 :: forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c FilePath)
    $cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c FilePath)
    dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c FilePath)
    $cgmapT :: (forall b. Data b => b -> b) -> FilePath -> FilePath
    gmapT :: (forall b. Data b => b -> b) -> FilePath -> FilePath
    $cgmapQl :: forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    gmapQl :: forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    $cgmapQr :: forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    gmapQr :: forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> FilePath -> r
    $cgmapQ :: forall u. (forall d. Data d => d -> u) -> FilePath -> [u]
    gmapQ :: forall u. (forall d. Data d => d -> u) -> FilePath -> [u]
    $cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> FilePath -> u
    gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> FilePath -> u
    $cgmapM :: forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    gmapM :: forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    $cgmapMp :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    gmapMp :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    $cgmapMo :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    gmapMo :: forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> FilePath -> m FilePath
    Data, Typeable)

    instance Eq FilePath where
      FilePath
    x == :: FilePath -> FilePath -> Bool
    == FilePath
    y = FilePath -> FilePath -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare FilePath
    x FilePath
    y Ordering -> Ordering -> Bool
    forall a. Eq a => a -> a -> Bool
    == Ordering
    EQ

    instance Ord FilePath where
      compare :: FilePath -> FilePath -> Ordering
    compare = (FilePath -> (Maybe Root, [Text], Maybe Text, [Text]))
    -> FilePath -> FilePath -> Ordering
    forall a b. Ord a => (b -> a) -> b -> b -> Ordering
    comparing (\FilePath
    p ->
        (FilePath -> Maybe Root
    pathRoot FilePath
    p
        , (String -> Text) -> [String] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap String -> Text
    unescape' (FilePath -> [String]
    pathDirectories FilePath
    p)
        , (String -> Text) -> Maybe String -> Maybe Text
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap String -> Text
    unescape' (FilePath -> Maybe String
    pathBasename FilePath
    p)
        , (String -> Text) -> [String] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap String -> Text
    unescape' (FilePath -> [String]
    pathExtensions FilePath
    p)
        ))

    instance NFData Root where
      rnf :: Root -> ()
    rnf (RootWindowsVolume Char
    c Bool
    extended) = Char -> ()
    forall a. NFData a => a -> ()
    rnf Char
    c () -> () -> ()
    forall a b. a -> b -> b
    `seq` Bool -> ()
    forall a. NFData a => a -> ()
    rnf Bool
    extended
      rnf (RootWindowsUnc String
    host String
    share Bool
    extended) = String -> ()
    forall a. NFData a => a -> ()
    rnf String
    host () -> () -> ()
    forall a b. a -> b -> b
    `seq` String -> ()
    forall a. NFData a => a -> ()
    rnf String
    share () -> () -> ()
    forall a b. a -> b -> b
    `seq` Bool -> ()
    forall a. NFData a => a -> ()
    rnf Bool
    extended
      rnf Root
    _ = ()

    instance NFData FilePath where
      rnf :: FilePath -> ()
    rnf FilePath
    p = Maybe Root -> ()
    forall a. NFData a => a -> ()
    rnf (FilePath -> Maybe Root
    pathRoot FilePath
    p) () -> () -> ()
    forall a b. a -> b -> b
    `seq` [String] -> ()
    forall a. NFData a => a -> ()
    rnf (FilePath -> [String]
    pathDirectories FilePath
    p) () -> () -> ()
    forall a b. a -> b -> b
    `seq` Maybe String -> ()
    forall a. NFData a => a -> ()
    rnf (FilePath -> Maybe String
    pathBasename FilePath
    p) () -> () -> ()
    forall a b. a -> b -> b
    `seq` [String] -> ()
    forall a. NFData a => a -> ()
    rnf (FilePath -> [String]
    pathExtensions FilePath
    p)

    -- | A file path with no root, directory, or filename
    empty :: FilePath
    empty :: FilePath
    empty = Maybe Root -> [String] -> Maybe String -> [String] -> FilePath
    FilePath Maybe Root
    forall a. Maybe a
    Nothing [] Maybe String
    forall a. Maybe a
    Nothing []

    dot :: Chunk
    dot :: String
    dot = String
    "."

    dots :: Chunk
    dots :: String
    dots = String
    ".."

    filenameChunk :: FilePath -> Chunk
    filenameChunk :: FilePath -> String
    filenameChunk FilePath
    p = [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat (String
    nameString -> [String] -> [String]
    forall a. a -> [a] -> [a]
    :[String]
    exts) where
      name :: String
    name = String -> ShowS -> Maybe String -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe String
    "" ShowS
    forall a. a -> a
    id (FilePath -> Maybe String
    pathBasename FilePath
    p)
      exts :: [String]
    exts = case FilePath -> [String]
    pathExtensions FilePath
    p of
        [] -> []
        [String]
    exts' -> String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    intersperse String
    dot (String
    ""String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    :[String]
    exts')

    rootChunk :: Maybe Root -> Chunk
    rootChunk :: Maybe Root -> String
    rootChunk Maybe Root
    r = ((Root -> String) -> Maybe Root -> String)
    -> Maybe Root -> (Root -> String) -> String
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip (String -> (Root -> String) -> Maybe Root -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe String
    "") Maybe Root
    r ((Root -> String) -> String) -> (Root -> String) -> String
    forall a b. (a -> b) -> a -> b
    $ \Root
    r' -> case Root
    r' of
      Root
    RootPosix -> String
    "/"
      RootWindowsVolume Char
    c Bool
    False -> Char
    c Char -> ShowS
    forall a. a -> [a] -> [a]
    : String
    ":\\"
      RootWindowsVolume Char
    c Bool
    True -> String
    "\\\\?\\" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ (Char
    c Char -> ShowS
    forall a. a -> [a] -> [a]
    : String
    ":\\")
      Root
    RootWindowsCurrentVolume -> String
    "\\"
      RootWindowsUnc String
    host String
    share Bool
    False -> String
    "\\\\" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    host String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    share
      RootWindowsUnc String
    host String
    share Bool
    True -> String
    "\\\\?\\UNC\\" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    host String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    "\\" String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ String
    share
      Root
    RootWindowsDoubleQMark -> String
    "\\??\\"

    rootText :: Maybe Root -> T.Text
    rootText :: Maybe Root -> Text
    rootText = String -> Text
    T.pack (String -> Text) -> (Maybe Root -> String) -> Maybe Root -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe Root -> String
    rootChunk

    directoryChunks :: FilePath -> [Chunk]
    directoryChunks :: FilePath -> [String]
    directoryChunks FilePath
    path = FilePath -> [String]
    pathDirectories FilePath
    path [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [FilePath -> String
    filenameChunk FilePath
    path]

    -------------------------------------------------------------------------------
    -- Rules
    -------------------------------------------------------------------------------

    -- | The type of @platformFormat@ for 'Rules' is conditionally selected at
    -- compilation time. As such it is only intended for direct use with external OS
    -- functions and code that expects @platformFormat@ to be stable across platforms
    -- may fail to subsequently compile on a differing platform.
    --
    -- For example: on Windows or OSX @platformFormat@ will be 'T.Text',
    -- and on Linux it will be 'B.ByteString'.
    --
    -- If portability is a concern, restrict usage to functions which do not expose
    -- @platformFormat@ directly.
    data Rules platformFormat = Rules
      { forall platformFormat. Rules platformFormat -> Text
    rulesName :: T.Text

      -- | Check if a 'FilePath' is valid; it must not contain any illegal
      -- characters, and must have a root appropriate to the current
      -- 'Rules'.
      , forall platformFormat. Rules platformFormat -> FilePath -> Bool
    valid :: FilePath -> Bool

      -- | Split a search path, such as @$PATH@ or @$PYTHONPATH@, into
      -- a list of 'FilePath's.
      --
      -- Note: The type of @platformTextFormat@ can change depending upon the
      -- underlying compilation platform. Consider using 'splitSearchPathString'
      -- instead. See 'Rules' for more information.
      , forall platformFormat.
    Rules platformFormat -> platformFormat -> [FilePath]
    splitSearchPath :: platformFormat -> [FilePath]

      -- | splitSearchPathString is like 'splitSearchPath', but takes a string
      -- encoded in the format used by @System.IO@.
      , forall platformFormat. Rules platformFormat -> String -> [FilePath]
    splitSearchPathString :: String -> [FilePath]

      -- | Attempt to convert a 'FilePath' to human&#x2010;readable text.
      --
      -- If the path is decoded successfully, the result is a 'Right'
      -- containing the decoded text. Successfully decoded text can be
      -- converted back to the original path using 'fromText'.
      --
      -- If the path cannot be decoded, the result is a 'Left' containing an
      -- approximation of the original path. If displayed to the user, this
      -- value should be accompanied by some warning that the path has an
      -- invalid encoding. Approximated text cannot be converted back to the
      -- original path.
      --
      -- This function ignores the user&#x2019;s locale, and assumes all
      -- file paths are encoded in UTF8. If you need to display file paths
      -- with an unusual or obscure encoding, use 'encode' and then decode
      -- them manually.
      --
      -- Since: 0.2
      , forall platformFormat.
    Rules platformFormat -> FilePath -> Either Text Text
    toText :: FilePath -> Either T.Text T.Text

      -- | Convert human&#x2010;readable text into a 'FilePath'.
      --
      -- This function ignores the user&#x2019;s locale, and assumes all
      -- file paths are encoded in UTF8. If you need to create file paths
      -- with an unusual or obscure encoding, encode them manually and then
      -- use 'decode'.
      --
      -- Since: 0.2
      , forall platformFormat. Rules platformFormat -> Text -> FilePath
    fromText :: T.Text -> FilePath

      -- | Convert a 'FilePath' to a platform&#x2010;specific format,
      -- suitable for use with external OS functions.
      --
      -- Note: The type of @platformTextFormat@ can change depending upon the
      -- underlying compilation platform. Consider using 'toText' or
      -- 'encodeString' instead. See 'Rules' for more information.
            --
      -- Since: 0.3
      , forall platformFormat.
    Rules platformFormat -> FilePath -> platformFormat
    encode :: FilePath -> platformFormat

      -- | Convert a 'FilePath' from a platform&#x2010;specific format,
      -- suitable for use with external OS functions.
      --
      -- Note: The type of @platformTextFormat@ can change depending upon the
      -- underlying compilation platform. Consider using 'fromText' or
      -- 'decodeString' instead. See 'Rules' for more information.
            --
      -- Since: 0.3
      , forall platformFormat.
    Rules platformFormat -> platformFormat -> FilePath
    decode :: platformFormat -> FilePath

      -- | Attempt to convert a 'FilePath' to a string suitable for use with
      -- functions in @System.IO@. The contents of this string are
      -- platform&#x2010;dependent, and are not guaranteed to be
      -- human&#x2010;readable. For converting 'FilePath's to a
      -- human&#x2010;readable format, use 'toText'.
      --
      -- Since: 0.3.1
      , forall platformFormat. Rules platformFormat -> FilePath -> String
    encodeString :: FilePath -> String

      -- | Attempt to parse a 'FilePath' from a string suitable for use
      -- with functions in @System.IO@. Do not use this function for parsing
      -- human&#x2010;readable paths, as the character set decoding is
      -- platform&#x2010;dependent. For converting human&#x2010;readable
      -- text to a 'FilePath', use 'fromText'.
      --
      -- Since: 0.3.1
      , forall platformFormat. Rules platformFormat -> String -> FilePath
    decodeString :: String -> FilePath
      }

    instance Show (Rules a) where
      showsPrec :: Int -> Rules a -> ShowS
    showsPrec Int
    d Rules a
    r = Bool -> ShowS -> ShowS
    showParen (Int
    d Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    10)
        (String -> ShowS
    showString String
    "Rules " ShowS -> ShowS -> ShowS
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> ShowS
    forall a. Show a => a -> ShowS
    shows (Rules a -> Text
    forall platformFormat. Rules platformFormat -> Text
    rulesName Rules a
    r))

    escape :: T.Text -> Chunk
    escape :: Text -> String
    escape Text
    t = Text -> String
    T.unpack Text
    t

    unescape :: Chunk -> (T.Text, Bool)
    unescape :: String -> (Text, Bool)
    unescape String
    cs = if (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (\Char
    c -> Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    0xDC80 Bool -> Bool -> Bool
    && Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0xDCFF) String
    cs
      then (String -> Text
    T.pack ((Char -> Char) -> ShowS
    forall a b. (a -> b) -> [a] -> [b]
    map (\Char
    c -> if Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    0xDC80 Bool -> Bool -> Bool
    && Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0xDCFF
        then Int -> Char
    chr (Char -> Int
    ord Char
    c Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    0xDC00)
        else Char
    c) String
    cs), Bool
    False)
      else (String -> Text
    T.pack String
    cs, Bool
    True)

    unescape' :: Chunk -> T.Text
    unescape' :: String -> Text
    unescape' = (Text, Bool) -> Text
    forall a b. (a, b) -> a
    fst ((Text, Bool) -> Text)
    -> (String -> (Text, Bool)) -> String -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> (Text, Bool)
    unescape

    unescapeBytes' :: Chunk -> B.ByteString
    unescapeBytes' :: String -> ByteString
    unescapeBytes' String
    cs = if (Char -> Bool) -> String -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    any (\Char
    c -> Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    0xDC80 Bool -> Bool -> Bool
    && Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0xDCFF) String
    cs
      then [ByteString] -> ByteString
    B8.concat ((Char -> ByteString) -> String -> [ByteString]
    forall a b. (a -> b) -> [a] -> [b]
    map (\Char
    c -> if Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    0xDC80 Bool -> Bool -> Bool
    && Char -> Int
    ord Char
    c Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0xDCFF
        then Char -> ByteString
    B8.singleton (Int -> Char
    chr (Char -> Int
    ord Char
    c Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    0xDC00))
        else Text -> ByteString
    TE.encodeUtf8 (Char -> Text
    T.singleton Char
    c)) String
    cs)
      else Text -> ByteString
    TE.encodeUtf8 (String -> Text
    T.pack String
    cs)

    splitBy :: (a -> Bool) -> [a] -> [[a]]
    splitBy :: forall a. (a -> Bool) -> [a] -> [[a]]
    splitBy a -> Bool
    p = [a] -> [[a]]
    loop where
      loop :: [a] -> [[a]]
    loop [a]
    xs = let
        ([a]
    chunk, [a]
    rest) = (a -> Bool) -> [a] -> ([a], [a])
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    break a -> Bool
    p [a]
    xs
        cont :: [[a]]
    cont = [a]
    chunk [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : [a] -> [[a]]
    loop ([a] -> [a]
    forall a. HasCallStack => [a] -> [a]
    tail [a]
    rest)
        in if [a] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [a]
    rest then [[a]
    chunk] else [[a]]
    cont

    textSplitBy :: (Char -> Bool) -> T.Text -> [T.Text]
    #if MIN_VERSION_text(0,11,0)
    textSplitBy :: (Char -> Bool) -> Text -> [Text]
    textSplitBy = (Char -> Bool) -> Text -> [Text]
    T.split
    #else
    textSplitBy = T.splitBy
    #endif

    parseFilename :: Chunk -> (Maybe Basename, [Extension])
    parseFilename :: String -> (Maybe String, [String])
    parseFilename String
    filename = (Maybe String, [String])
    parsed where
      parsed :: (Maybe String, [String])
    parsed = if String -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null String
    filename
        then (Maybe String
    forall a. Maybe a
    Nothing, [])
        else case (Char -> Bool) -> String -> (String, String)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    span (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '.') String
    filename of
          (String
    leadingDots, String
    baseAndExts) -> case (Char -> Bool) -> String -> [String]
    forall a. (a -> Bool) -> [a] -> [[a]]
    splitBy (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '.') String
    baseAndExts of
            [] -> (String -> String -> Maybe String
    forall {a}. [a] -> [a] -> Maybe [a]
    joinDots String
    leadingDots String
    "", [])
            (String
    name':[String]
    exts') -> (String -> String -> Maybe String
    forall {a}. [a] -> [a] -> Maybe [a]
    joinDots String
    leadingDots String
    name', [String]
    exts')
      joinDots :: [a] -> [a] -> Maybe [a]
    joinDots [a]
    leadingDots [a]
    base = case [a]
    leadingDots [a] -> [a] -> [a]
    forall a. [a] -> [a] -> [a]
    ++ [a]
    base of
        [] -> Maybe [a]
    forall a. Maybe a
    Nothing
        [a]
    joined -> [a] -> Maybe [a]
    forall a. a -> Maybe a
    Just [a]
    joined

    maybeDecodeUtf8 :: B.ByteString -> Maybe T.Text
    maybeDecodeUtf8 :: ByteString -> Maybe Text
    maybeDecodeUtf8 ByteString
    bytes = case ByteString -> Either UnicodeException Text
    TE.decodeUtf8' ByteString
    bytes of
      Left UnicodeException
    _ -> Maybe Text
    forall a. Maybe a
    Nothing
      Right Text
    text -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    text