handsy (empty) → 0.0.1
raw patch · 4 files changed
+100/−0 lines, 4 filesdep +basedep +bytestringdep +freesetup-changed
Dependencies added: base, bytestring, free, process, process-extras, transformers
Files
- Setup.hs +2/−0
- handsy.cabal +26/−0
- src/System/Handsy.hs +38/−0
- src/System/Handsy/Remote.hs +34/−0
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ handsy.cabal view
@@ -0,0 +1,26 @@+name: handsy+version: 0.0.1+synopsis: A DSL to describe common shell operations and interpeters for running them locally and remotely.+-- description: +license: BSD3+author: Utku Demir+maintainer: utdemir@gmail.com+category: System+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/utdemir/master++library+ exposed-modules: System.Handsy+ System.Handsy.Remote+ build-depends: base >=4.7 && <4.8+ , bytestring+ , process+ , transformers+ , free+ , process-extras + hs-source-dirs: src+ default-language: Haskell2010
+ src/System/Handsy.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++module System.Handsy where++import Prelude hiding (readFile, writeFile)++import Control.Monad.IO.Class+import qualified Data.ByteString.Char8 as B+import System.Exit++import Control.Monad.Free.TH+import Control.Monad.Trans.Free+import System.Process.ByteString++-- | Base functor for our dsl+data HandsyF k = Command String [String] B.ByteString ((ExitCode, B.ByteString, B.ByteString) -> k)+ | ReadFile String (B.ByteString -> k)+ | WriteFile String B.ByteString (() -> k)+ deriving (Functor)++makeFree ''HandsyF++type Handsy = FreeT HandsyF IO++run :: Handsy a -> IO a+run h = do+ x <- runFreeT h+ case x of+ Pure r -> return r+ Free (ReadFile fp next)+ -> B.readFile fp >>= run . next+ Free (WriteFile fp str next)+ -> B.writeFile fp str >>= run . next+ Free (Command prg args stdin next)+ -> readProcessWithExitCode prg args stdin >>= run . next
+ src/System/Handsy/Remote.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}++module System.Handsy.Remote where++import Prelude hiding (readFile, writeFile)++import System.Handsy++import qualified Data.ByteString.Char8 as B++import Control.Monad.IO.Class+import Control.Monad.Trans.Free++data RemoteOptions =+ RemoteOptions {+ sshCommand :: (String, [String])+ }++runRemote :: RemoteOptions -> Handsy a -> IO a+runRemote opts h = do+ x <- runFreeT h+ case x of+ Pure r -> return r+ Free (ReadFile fp next) -> do+ (_, stdin, _) <- runSsh "cat" [fp] ""+ runRemote opts (next stdin)+ Free (WriteFile fp str next) -> do+ _ <- runSsh "dd" ["of=" ++ fp] str -- TODO: Escape necessary?+ runRemote opts (next ())+ Free (Command prg args stdin next) -> runSsh prg args stdin >>= run . next++ where+ (ssh, sshOpts) = sshCommand opts+ runSsh prg args stdin = run (command ssh (sshOpts ++ prg : args) stdin)