{-# LANGUAGE FlexibleInstances #-}

module System.Linux.Btrfs.FilePathLike
    ( FilePathLike(..)
    , RawFilePath
    ) where

import Data.String
import Data.Monoid
import Data.List (dropWhileEnd)
import Foreign.C.String (CString, CStringLen)
import qualified System.Posix.Internals as P
import GHC.IO.Encoding (getFileSystemEncoding)
import qualified GHC.Foreign as GHC
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Char8 as B8
import System.Posix.Types (Fd, FileMode)
import System.Posix.IO hiding (openFd)
import System.Posix.ByteString.FilePath (RawFilePath)
import qualified System.Posix.IO as S (openFd)
import qualified System.Posix.IO.ByteString as B (openFd)
import System.IO.Unsafe (unsafePerformIO)
import Prelude

class (Monoid s, IsString s) => FilePathLike s where
    asString :: s -> String
    peekFilePath :: CString -> IO s
    peekFilePathLen :: CStringLen -> IO s
    withFilePath :: s -> (CString -> IO a) -> IO a
    withFilePathLen :: s -> (CStringLen -> IO a) -> IO a
    unsafeWithFilePathLen :: s -> (CStringLen -> IO a) -> IO a
    openFd :: s -> OpenMode -> OpenFileFlags -> IO Fd
    dropTrailingSlash :: s -> s
    (</>) :: s -> s -> s
    splitFileName :: s -> (s, s)

instance FilePathLike [Char] where
    asString :: [Char] -> [Char]
asString = forall a. a -> a
id
    peekFilePath :: CString -> IO [Char]
peekFilePath = CString -> IO [Char]
P.peekFilePath
    peekFilePathLen :: CStringLen -> IO [Char]
peekFilePathLen = CStringLen -> IO [Char]
P.peekFilePathLen
    withFilePath :: forall a. [Char] -> (CString -> IO a) -> IO a
withFilePath = forall a. [Char] -> (CString -> IO a) -> IO a
P.withFilePath
    withFilePathLen :: forall a. [Char] -> (CStringLen -> IO a) -> IO a
withFilePathLen [Char]
path CStringLen -> IO a
action =
        IO TextEncoding
getFileSystemEncoding forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \TextEncoding
enc ->
            forall a. TextEncoding -> [Char] -> (CStringLen -> IO a) -> IO a
GHC.withCStringLen TextEncoding
enc [Char]
path CStringLen -> IO a
action
    unsafeWithFilePathLen :: forall a. [Char] -> (CStringLen -> IO a) -> IO a
unsafeWithFilePathLen = forall s a. FilePathLike s => s -> (CStringLen -> IO a) -> IO a
withFilePathLen
    openFd :: [Char] -> OpenMode -> OpenFileFlags -> IO Fd
openFd = [Char] -> OpenMode -> OpenFileFlags -> IO Fd
S.openFd
    dropTrailingSlash :: [Char] -> [Char]
dropTrailingSlash [Char]
s = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
s' then [Char]
"/" else [Char]
s'
      where
        s' :: [Char]
s' = forall a. (a -> Bool) -> [a] -> [a]
dropWhileEnd (forall a. Eq a => a -> a -> Bool
== Char
'/') [Char]
s
    [Char]
_ </> :: [Char] -> [Char] -> [Char]
</> s2 :: [Char]
s2@(Char
'/' : [Char]
_) = [Char]
s2
    [Char]
s1 </> [Char]
"" = [Char]
s1
    [Char]
"" </> [Char]
s2 = [Char]
s2
    [Char]
s1 </> [Char]
s2 = if [Char]
s1' forall a. Eq a => a -> a -> Bool
== [Char]
"/" then Char
'/' forall a. a -> [a] -> [a]
: [Char]
s2 else [Char]
s1' forall a. [a] -> [a] -> [a]
++ [Char]
"/" forall a. [a] -> [a] -> [a]
++ [Char]
s2
      where
        s1' :: [Char]
s1' = forall s. FilePathLike s => s -> s
dropTrailingSlash [Char]
s1
    splitFileName :: [Char] -> ([Char], [Char])
splitFileName [Char]
s = (if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
d then [Char]
"./" else forall a. [a] -> [a]
reverse [Char]
d, forall a. [a] -> [a]
reverse [Char]
n)
      where
        ([Char]
n, [Char]
d) = forall a. (a -> Bool) -> [a] -> ([a], [a])
span (forall a. Eq a => a -> a -> Bool
/= Char
'/') forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse [Char]
s

instance FilePathLike B.ByteString where
    asString :: ByteString -> [Char]
asString = forall s1 s2. (FilePathLike s1, FilePathLike s2) => s1 -> s2
convert
    peekFilePath :: CString -> IO ByteString
peekFilePath = CString -> IO ByteString
B.packCString
    peekFilePathLen :: CStringLen -> IO ByteString
peekFilePathLen = CStringLen -> IO ByteString
B.packCStringLen
    withFilePath :: forall a. ByteString -> (CString -> IO a) -> IO a
withFilePath = forall a. ByteString -> (CString -> IO a) -> IO a
B.useAsCString
    withFilePathLen :: forall a. ByteString -> (CStringLen -> IO a) -> IO a
withFilePathLen = forall a. ByteString -> (CStringLen -> IO a) -> IO a
B.useAsCStringLen
    unsafeWithFilePathLen :: forall a. ByteString -> (CStringLen -> IO a) -> IO a
unsafeWithFilePathLen = forall a. ByteString -> (CStringLen -> IO a) -> IO a
B.unsafeUseAsCStringLen
    openFd :: ByteString -> OpenMode -> OpenFileFlags -> IO Fd
openFd = ByteString -> OpenMode -> OpenFileFlags -> IO Fd
B.openFd
    dropTrailingSlash :: ByteString -> ByteString
dropTrailingSlash ByteString
s = if ByteString -> Bool
B.null ByteString
s' then ByteString
slashBS else ByteString
s'
      where
        (ByteString
s', ByteString
_) = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
B8.spanEnd (forall a. Eq a => a -> a -> Bool
== Char
'/') ByteString
s
    ByteString
s1 </> :: ByteString -> ByteString -> ByteString
</> ByteString
s2
        | ByteString -> Bool
B.null ByteString
s1 = ByteString
s2
        | ByteString -> Bool
B.null ByteString
s2 = ByteString
s1
        | ByteString -> Char
B8.head ByteString
s2 forall a. Eq a => a -> a -> Bool
== Char
'/' = ByteString
s2
        | Bool
otherwise =
            let s1' :: ByteString
s1' = forall s. FilePathLike s => s -> s
dropTrailingSlash ByteString
s1
            in  if ByteString -> Char
B8.last ByteString
s1' forall a. Eq a => a -> a -> Bool
== Char
'/' then ByteString
slashBS forall a. Semigroup a => a -> a -> a
<> ByteString
s2
                                      else [ByteString] -> ByteString
B.concat [ByteString
s1', ByteString
slashBS, ByteString
s2]
    splitFileName :: ByteString -> (ByteString, ByteString)
splitFileName ByteString
s = (if ByteString -> Bool
B.null ByteString
d then ByteString
curDir else ByteString
d, ByteString
n)
      where
        (ByteString
d, ByteString
n) = (Char -> Bool) -> ByteString -> (ByteString, ByteString)
B8.spanEnd (forall a. Eq a => a -> a -> Bool
/= Char
'/') ByteString
s
        curDir :: ByteString
curDir = [Char] -> ByteString
B8.pack [Char]
"./"

convert :: (FilePathLike s1, FilePathLike s2) => s1 -> s2
convert :: forall s1 s2. (FilePathLike s1, FilePathLike s2) => s1 -> s2
convert s1
s = forall a. IO a -> a
unsafePerformIO forall a b. (a -> b) -> a -> b
$ forall s a. FilePathLike s => s -> (CStringLen -> IO a) -> IO a
unsafeWithFilePathLen s1
s forall s. FilePathLike s => CStringLen -> IO s
peekFilePathLen

slashBS :: B.ByteString
slashBS :: ByteString
slashBS = Char -> ByteString
B8.singleton Char
'/'