module Hedgehog.Extras.Stock.IO.Network.Sprocket
  ( Sprocket(..)
  , doesSprocketExist
  , sprocketArgumentName
  , sprocketSystemName
  , maxSprocketArgumentNameLength
  ) where

import           Data.Bool
import           Data.Char
import           Data.Eq
import           Data.Functor
import           Data.Int
import           Data.Semigroup
import           Data.String (String)
import           Hedgehog.Extras.Stock.OS
import           System.IO (FilePath, IO)
import           Text.Show

import qualified Hedgehog.Extras.Stock.IO.Network.NamedPipe as IO
import qualified Hedgehog.Extras.Stock.IO.Network.Socket as IO

-- | Socket emulation.  On Posix it represents a socket.  On Windows it represents a named pipe.
data Sprocket = Sprocket
  { Sprocket -> String
sprocketBase :: String
  , Sprocket -> String
sprocketName :: String
  } deriving (Sprocket -> Sprocket -> Bool
(Sprocket -> Sprocket -> Bool)
-> (Sprocket -> Sprocket -> Bool) -> Eq Sprocket
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Sprocket -> Sprocket -> Bool
$c/= :: Sprocket -> Sprocket -> Bool
== :: Sprocket -> Sprocket -> Bool
$c== :: Sprocket -> Sprocket -> Bool
Eq, Int -> Sprocket -> ShowS
[Sprocket] -> ShowS
Sprocket -> String
(Int -> Sprocket -> ShowS)
-> (Sprocket -> String) -> ([Sprocket] -> ShowS) -> Show Sprocket
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Sprocket] -> ShowS
$cshowList :: [Sprocket] -> ShowS
show :: Sprocket -> String
$cshow :: Sprocket -> String
showsPrec :: Int -> Sprocket -> ShowS
$cshowsPrec :: Int -> Sprocket -> ShowS
Show)

-- | Test if the sprocket exists
doesSprocketExist :: Sprocket -> IO Bool
doesSprocketExist :: Sprocket -> IO Bool
doesSprocketExist Sprocket
socket = if Bool
isWin32
  then String -> IO Bool
IO.doesNamedPipeExist (Sprocket -> String
sprocketSystemName Sprocket
socket)
  else String -> IO Bool
IO.doesSocketExist (Sprocket -> String
sprocketSystemName Sprocket
socket)

-- | Use this to query the OS about the sprocket
sprocketSystemName :: Sprocket -> FilePath
sprocketSystemName :: Sprocket -> String
sprocketSystemName sprocket :: Sprocket
sprocket@(Sprocket String
base String
name) = if Bool
isWin32
  then Sprocket -> String
sprocketNamedPipeName Sprocket
sprocket
  else String
base String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"/" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
name

-- | Use this when needing to pass a sprocket into a command line argument.
sprocketArgumentName :: Sprocket -> FilePath
sprocketArgumentName :: Sprocket -> String
sprocketArgumentName sprocket :: Sprocket
sprocket@(Sprocket String
_ String
name) = if Bool
isWin32
  then Sprocket -> String
sprocketNamedPipeName Sprocket
sprocket
  else String
name

maxSprocketArgumentNameLength :: Int
maxSprocketArgumentNameLength :: Int
maxSprocketArgumentNameLength = if Bool
isWin32
  then Int
256
  else Int
104

-- | The named pipe name of the sprocket on Win32 systems
sprocketNamedPipeName :: Sprocket -> FilePath
sprocketNamedPipeName :: Sprocket -> String
sprocketNamedPipeName (Sprocket String
_ String
name) = String
"\\\\.\\pipe" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> ShowS
dedupBackslash (String
"\\" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> (Char -> Char) -> ShowS
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
slackToBack String
name)
  where slackToBack :: Char -> Char
        slackToBack :: Char -> Char
slackToBack Char
c = if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/' then Char
'\\' else Char
c
        dedupBackslash :: String -> String
        dedupBackslash :: ShowS
dedupBackslash (Char
'\\':Char
'\\':String
xs) = ShowS
dedupBackslash (Char
'\\'Char -> ShowS
forall a. a -> [a] -> [a]
:String
xs)
        dedupBackslash (Char
x:String
xs) = Char
xChar -> ShowS
forall a. a -> [a] -> [a]
:ShowS
dedupBackslash String
xs
        dedupBackslash [] = []