packages feed

rz-pipe-0.1.0: RzPipe/RzPipe.hs

module RzPipe (RzContext(), open, cmd, cmdj) where
import Data.Char
import Data.Word
import Network.HTTP
import System.IO
import System.Process
import System.Environment (getEnv)
import GHC.IO.Handle.FD
import System.Posix.Internals (FD)
import qualified Data.Aeson as JSON
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.UTF8 as U

withPipes p = p { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }

createProcess' args = fmap f $ createProcess (withPipes args) where
    f (Just i, Just o, Just e, h) = (i, o, e, h)
    f _ = error "createProcess': Failed to open pipes to the subprocess."

lHTakeWhile :: (Word8 -> Bool) -> Handle -> IO B.ByteString
lHTakeWhile p h = do
    c <- fmap B.head $ B.hGet h 1
    if p c
        then fmap (c `B.cons`) $ lHTakeWhile p h
        else return B.empty

data RzContext = HttpCtx String
               | PipeCtx Handle Handle

open :: Maybe String -> IO RzContext
open (Just url@('h':'t':'t':'p':_)) = return $ HttpCtx (url ++ "/cmd/")
open (Just filename) = do
    (hIn, hOut, _, _) <- createProcess' $ proc "rizin" ["-q0", filename]
    lHTakeWhile (/= 0) hOut -- drop the inital null that rizin emits
    return $ PipeCtx hIn hOut
open Nothing = do
    hIn <- fdToHandle =<< (read::(String -> FD)) <$> getEnv "RZ_PIPE_OUT"
    hOut <- fdToHandle =<< (read::(String -> FD)) <$> getEnv "RZ_PIPE_IN"
    return $ PipeCtx hIn hOut

cmdHttp :: String -> String -> IO String
cmdHttp url cmd = getResponseBody =<< simpleHTTP (getRequest (url ++ urlEncode cmd))

cmdPipe :: Handle -> Handle -> String -> IO B.ByteString
cmdPipe hIn hOut cmd = hPutStrLn hIn cmd >> hFlush hIn >> lHTakeWhile (/= 0) hOut

cmdB :: RzContext -> String -> IO B.ByteString
cmdB (HttpCtx url) cmd = U.fromString <$> cmdHttp url cmd
cmdB (PipeCtx hIn hOut) cmd = cmdPipe hIn hOut cmd

cmd :: RzContext -> String -> IO String
cmd (HttpCtx url) cmd = cmdHttp url cmd
cmd (PipeCtx hIn hOut) cmd = U.toString <$> cmdPipe hIn hOut cmd

cmdj :: JSON.FromJSON a => RzContext -> String -> IO (Maybe a)
cmdj = (fmap JSON.decode .) . cmdB