{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module System.Hapistrano.Commands.Internal where
import Control.Monad.IO.Class
import Data.Char (isSpace)
import Data.List (dropWhileEnd)
import Data.Maybe (catMaybes, fromJust, mapMaybe)
import Data.Proxy
import Numeric.Natural
import Path
import System.Hapistrano.Types (TargetSystem (..))
class Command a where
type Result a :: *
renderCommand :: a -> String
parseResult :: Proxy a -> String -> Result a
data Whoami =
Whoami
deriving (Show, Eq, Ord)
instance Command Whoami where
type Result Whoami = String
renderCommand Whoami = "whoami"
parseResult Proxy = trim
data Cd cmd =
Cd (Path Abs Dir) cmd
instance Command cmd => Command (Cd cmd) where
type Result (Cd cmd) = Result cmd
renderCommand (Cd path cmd) =
"(cd " ++ quoteCmd (fromAbsDir path) ++ " && " ++ renderCommand cmd ++ ")"
parseResult Proxy = parseResult (Proxy :: Proxy cmd)
data MkDir =
MkDir (Path Abs Dir)
instance Command MkDir where
type Result MkDir = ()
renderCommand (MkDir path) =
formatCmd "mkdir" [Just "-pv", Just (fromAbsDir path)]
parseResult Proxy _ = ()
data Rm where
Rm :: Path Abs t -> Rm
instance Command Rm where
type Result Rm = ()
renderCommand (Rm path) = formatCmd "rm" [Just "-rf", Just (toFilePath path)]
parseResult Proxy _ = ()
data Mv t =
Mv TargetSystem (Path Abs t) (Path Abs t)
instance Command (Mv File) where
type Result (Mv File) = ()
renderCommand (Mv ts old new) =
formatCmd "mv" [Just flags, Just (fromAbsFile old), Just (fromAbsFile new)]
where
flags =
if isLinux ts
then "-fvT"
else "-fv"
parseResult Proxy _ = ()
instance Command (Mv Dir) where
type Result (Mv Dir) = ()
renderCommand (Mv _ old new) =
formatCmd "mv" [Just "-fv", Just (fromAbsDir old), Just (fromAbsDir new)]
parseResult Proxy _ = ()
data Ln where
Ln :: TargetSystem -> Path Abs t -> Path Abs File -> Ln
instance Command Ln where
type Result Ln = ()
renderCommand (Ln ts target linkName) =
formatCmd
"ln"
[Just flags, Just (toFilePath target), Just (fromAbsFile linkName)]
where
flags =
if isLinux ts
then "-svT"
else "-sv"
parseResult Proxy _ = ()
data Readlink t =
Readlink TargetSystem (Path Abs File)
instance Command (Readlink File) where
type Result (Readlink File) = Path Abs File
renderCommand (Readlink ts path) =
formatCmd "readlink" [flags, Just (toFilePath path)]
where
flags =
if isLinux ts
then Just "-f"
else Nothing
parseResult Proxy = fromJust . parseAbsFile . trim
instance Command (Readlink Dir) where
type Result (Readlink Dir) = Path Abs Dir
renderCommand (Readlink ts path) =
formatCmd "readlink" [flags, Just (toFilePath path)]
where
flags =
if isLinux ts
then Just "-f"
else Nothing
parseResult Proxy = fromJust . parseAbsDir . trim
data Ls =
Ls (Path Abs Dir)
instance Command Ls where
type Result Ls = ()
renderCommand (Ls path) = formatCmd "ls" [Just (fromAbsDir path)]
parseResult Proxy _ = ()
data Find t =
Find Natural (Path Abs Dir)
instance Command (Find Dir) where
type Result (Find Dir) = [Path Abs Dir]
renderCommand (Find maxDepth dir) =
formatCmd
"find"
[ Just (fromAbsDir dir)
, Just "-maxdepth"
, Just (show maxDepth)
, Just "-type"
, Just "d"
]
parseResult Proxy = mapMaybe (parseAbsDir . trim) . lines
instance Command (Find File) where
type Result (Find File) = [Path Abs File]
renderCommand (Find maxDepth dir) =
formatCmd
"find"
[ Just (fromAbsDir dir)
, Just "-maxdepth"
, Just (show maxDepth)
, Just "-type"
, Just "f"
]
parseResult Proxy = mapMaybe (parseAbsFile . trim) . lines
data Touch =
Touch (Path Abs File)
instance Command Touch where
type Result Touch = ()
renderCommand (Touch path) = formatCmd "touch" [Just (fromAbsFile path)]
parseResult Proxy _ = ()
data GitCheckout =
GitCheckout String
instance Command GitCheckout where
type Result GitCheckout = ()
renderCommand (GitCheckout revision) =
formatCmd "git" [Just "checkout", Just revision]
parseResult Proxy _ = ()
data GitClone =
GitClone Bool (Either String (Path Abs Dir)) (Path Abs Dir)
instance Command GitClone where
type Result GitClone = ()
renderCommand (GitClone bare src dest) =
formatCmd
"git"
[ Just "clone"
, if bare
then Just "--bare"
else Nothing
, Just
(case src of
Left repoUrl -> repoUrl
Right srcPath -> fromAbsDir srcPath)
, Just (fromAbsDir dest)
]
parseResult Proxy _ = ()
data GitFetch =
GitFetch String
instance Command GitFetch where
type Result GitFetch = ()
renderCommand (GitFetch remote) =
formatCmd
"git"
[Just "fetch", Just remote, Just "+refs/heads/\\*:refs/heads/\\*"]
parseResult Proxy _ = ()
data GitReset =
GitReset String
instance Command GitReset where
type Result GitReset = ()
renderCommand (GitReset revision) =
formatCmd "git" [Just "reset", Just revision]
parseResult Proxy _ = ()
data GenericCommand =
GenericCommand String
deriving (Show, Eq, Ord)
instance Command GenericCommand where
type Result GenericCommand = ()
renderCommand (GenericCommand cmd) = cmd
parseResult Proxy _ = ()
mkGenericCommand :: String -> Maybe GenericCommand
mkGenericCommand str =
if '\n' `elem` str' || null str'
then Nothing
else Just (GenericCommand str')
where
str' = trim (takeWhile (/= '#') str)
unGenericCommand :: GenericCommand -> String
unGenericCommand (GenericCommand x) = x
readScript :: MonadIO m => Path Abs File -> m [GenericCommand]
readScript path =
liftIO $ mapMaybe mkGenericCommand . lines <$> readFile (fromAbsFile path)
formatCmd :: String -> [Maybe String] -> String
formatCmd cmd args = unwords (quoteCmd <$> (cmd : catMaybes args))
quoteCmd :: String -> String
quoteCmd str =
if any isSpace str
then "\"" ++ str ++ "\""
else str
trim :: String -> String
trim = dropWhileEnd isSpace . dropWhile isSpace
isLinux :: TargetSystem -> Bool
isLinux = (== GNULinux)