Backend.hs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Walrus.Backend (
  3. Backend(..),
  4. module Walrus.Backend.Metadata,
  5. parseBackend,
  6. runBackend
  7. ) where
  8. import qualified Data.Attoparsec.ByteString.Char8 as A
  9. import qualified Data.Attoparsec.ByteString.Lazy as LA
  10. import Data.Attoparsec.ByteString.Char8.Extras
  11. import qualified Data.ByteString.Lazy as LBS
  12. import qualified Data.Char as C
  13. import Data.Textual.Class
  14. import Walrus.Backend.Metadata
  15. import Control.Applicative
  16. import Network
  17. import System.IO
  18. --import System.IO.Uniform
  19. --import System.IO.Uniform.HandlePair as HP
  20. --import System.IO.Uniform.Network as Net
  21. import qualified System.Process as P
  22. data Backend = TCPBackend String Int |
  23. ExecBackend String [String] deriving (Show, Read, Ord, Eq)
  24. parseBackend :: A.Parser Backend
  25. parseBackend = do
  26. A.choice [
  27. do
  28. tp "tcp"
  29. h <- tillSpace
  30. p <- A.decimal
  31. return $ TCPBackend h p,
  32. do
  33. tp "exec"
  34. f <- qStr
  35. skipHorizontalSpace
  36. pp <- parseParameters
  37. return $ ExecBackend f pp
  38. ]
  39. where
  40. tp t = do
  41. skipHorizontalSpace
  42. A.stringCI t
  43. skipHorizontalSpace
  44. tillSpace :: A.Parser String
  45. tillSpace = fromTextual <$> A.takeTill C.isSpace
  46. parseParameters :: A.Parser [String]
  47. parseParameters = do
  48. p <- qStr
  49. skipHorizontalSpace
  50. if null p then return [] else do
  51. pp <- parseParameters
  52. return $ p : pp
  53. qStr :: A.Parser String
  54. qStr = fromTextual <$> quotedString '\\' " " ""
  55. runBackend :: Backend -> (Metadata, LBS.ByteString) -> IO (Either String (Metadata, LBS.ByteString))
  56. runBackend b (m, qdt) = do
  57. let rm = renderMetadata m
  58. edt' <- intBk b $ LBS.concat [
  59. fromTextual rm,
  60. qdt]
  61. case LA.parse repParse edt' of
  62. LA.Fail _ _ e -> return $ Left e
  63. LA.Done edt m' -> return $ Right (m', edt)
  64. where
  65. intBk :: Backend -> LBS.ByteString -> IO LBS.ByteString
  66. intBk (TCPBackend h p) = runTcp h p
  67. intBk (ExecBackend f aa) = runExec f aa
  68. repParse = do
  69. m' <- parseMetadata
  70. return m'
  71. runTcp :: String -> Int -> LBS.ByteString -> IO LBS.ByteString
  72. runTcp host port dt = do
  73. h <- connectTo host (PortNumber . fromIntegral $ port)
  74. bkgTrans h dt
  75. bkgTrans :: Handle -> LBS.ByteString -> IO LBS.ByteString
  76. bkgTrans h dt = do
  77. hSetNewlineMode h noNewlineTranslation
  78. LBS.hPut h dt
  79. hFlush h
  80. LBS.hGetContents h
  81. runExec :: String -> [String] -> LBS.ByteString -> IO LBS.ByteString
  82. runExec f args dt = do
  83. (Just i, Just o, _, _) <- P.createProcess (
  84. (P.proc f args){P.std_in=P.CreatePipe}{
  85. P.std_out=P.CreatePipe}{P.std_err=P.Inherit}
  86. )
  87. mapM (\h -> hSetNewlineMode h noNewlineTranslation) [i, o]
  88. LBS.hPut i dt
  89. hFlush i
  90. LBS.hGetContents o