{-# LANGUAGE OverloadedStrings
           , FlexibleInstances
           , StandaloneDeriving #-}
{-| Utilities for working with shell script.
 -}
module System.Posix.ARX.Sh ( Val(), val, Var(), var,
                             setEU, Render(..), Raw(..) ) where

import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as Bytes
import Data.Monoid

import qualified Blaze.ByteString.Builder as Blaze
import qualified Text.ShellEscape as Esc


setEU                       ::  Blaze.Builder
setEU :: Builder
setEU                        =  Builder
"set -e -u\n"

{-| Valid shell string values contain any byte but null.
 -}
newtype Val                  =  Val ByteString
deriving instance Eq Val
deriving instance Ord Val
deriving instance Show Val
instance Render Val where
  render :: Val -> Builder
render (Val ByteString
bytes) = (ByteString -> Builder
Blaze.fromByteString (ByteString -> Builder)
-> (ByteString -> ByteString) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Sh -> ByteString
forall t. Escape t => t -> ByteString
Esc.bytes (Sh -> ByteString)
-> (ByteString -> Sh) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Sh
Esc.sh) ByteString
bytes
instance Raw Val where
  raw :: Val -> ByteString
raw (Val ByteString
bytes)            =  ByteString
bytes

val                         ::  ByteString -> Maybe Val
val :: ByteString -> Maybe Val
val ByteString
bytes = Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard ((Char -> Bool) -> ByteString -> Bool
Bytes.all (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\0') ByteString
bytes) Maybe () -> Maybe Val -> Maybe Val
forall a b. Maybe a -> Maybe b -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Val -> Maybe Val
forall a. a -> Maybe a
Just (ByteString -> Val
Val ByteString
bytes)

{-| Valid shell variable names consist of a leading letter or underscore and
    then any number of letters, underscores or digits.
 -}
newtype Var                  =  Var ByteString
deriving instance Eq Var
deriving instance Ord Var
deriving instance Show Var
instance Render Var where
  render :: Var -> Builder
render (Var ByteString
bytes)         =  ByteString -> Builder
Blaze.fromByteString ByteString
bytes
instance Raw Var where
  raw :: Var -> ByteString
raw (Var ByteString
bytes)            =  ByteString
bytes

var                         ::  ByteString -> Maybe Var
var :: ByteString -> Maybe Var
var ByteString
""                       =  Maybe Var
forall a. Maybe a
Nothing
var ByteString
bytes = Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Char -> Bool
leading Char
h Bool -> Bool -> Bool
&& (Char -> Bool) -> ByteString -> Bool
Bytes.all Char -> Bool
body ByteString
t) Maybe () -> Maybe Var -> Maybe Var
forall a b. Maybe a -> Maybe b -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Var -> Maybe Var
forall a. a -> Maybe a
Just (ByteString -> Var
Var ByteString
bytes)
 where
  (Char
h, ByteString
t)                     =  (ByteString -> Char
Bytes.head ByteString
bytes, HasCallStack => ByteString -> ByteString
ByteString -> ByteString
Bytes.tail ByteString
bytes)
  body :: Char -> Bool
body Char
c                     =  Char -> Bool
leading Char
c Bool -> Bool -> Bool
|| (Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'0' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9')
  leading :: Char -> Bool
leading Char
c = (Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'A' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'Z') Bool -> Bool -> Bool
|| (Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'a' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'z') Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_'

instance Render [(Var, Val)] where
  render :: [(Var, Val)] -> Builder
render [       ]           =  Builder
forall a. Monoid a => a
mempty
  render ((Var
k,Val
v):[(Var, Val)]
t)           =  Builder
exportStatement Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
`mappend` [(Var, Val)] -> Builder
forall t. Render t => t -> Builder
render [(Var, Val)]
t
   where
    exportStatement :: Builder
exportStatement = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat [Builder
"export ", Var -> Builder
forall t. Render t => t -> Builder
render Var
k, Builder
"=", Val -> Builder
forall t. Render t => t -> Builder
render Val
v, Builder
"\n"]

instance Render [Val] where
  render :: [Val] -> Builder
render                     =  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> ([Val] -> [Builder]) -> [Val] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Val -> Builder) -> [Val] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
mappend Builder
" " (Builder -> Builder) -> (Val -> Builder) -> Val -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Val -> Builder
forall t. Render t => t -> Builder
render)

class Render t where
  render                    ::  t -> Blaze.Builder

class Raw t where
  raw                       ::  t -> ByteString