packages feed

phoityne-vscode-0.0.21.0: app/Phoityne/VSCode/Control.hs

{-# LANGUAGE GADTs               #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE MultiWayIf          #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable  #-}

module Phoityne.VSCode.Control (
  run
) where

import System.IO
import Control.Concurrent
import Text.Parsec
import Control.Lens
import qualified System.Log.Logger as L

import Phoityne.VSCode.Constant
import Phoityne.VSCode.Utility
import Phoityne.VSCode.Type
import qualified Phoityne.VSCode.Argument as A
import qualified Phoityne.VSCode.Core as C
import qualified Data.ByteString.Lazy as BSL
import System.Exit
import qualified Control.Exception.Safe as E


-- |
-- 
_CONTENT_LENGTH :: String
_CONTENT_LENGTH = "Content-Length: " 


-- |
-- 
run :: IO Int
run = flip E.catches handlers $
  E.finally go finalize

  where
    handlers = [ E.Handler helpExcept
               , E.Handler exitExcept
               , E.Handler ioExcept
               , E.Handler someExcept
               ]
    finalize = L.removeAllHandlers 
    helpExcept (_ :: A.HelpExitException) = return 0 
    exitExcept ExitSuccess                = return 0
    exitExcept (ExitFailure c)            = return c
    ioExcept   (e :: E.IOException)       = print e >> return 1
    someExcept (e :: E.SomeException)     = print e >> return 1


-- |
-- 
go :: IO Int
go = do

  args <- A.getArgData

  hSetBuffering stdin NoBuffering
  hSetEncoding  stdin utf8

  hSetBuffering stdout NoBuffering
  hSetEncoding  stdout utf8

  mvarDat <- newMVar C.defaultDebugContextData {
                       C.responseHandlerDebugContextData = sendResponse
                     , C.hackagePackageVersionDebugContextData = args ^. hackageVersionArgData
                     }

  wait mvarDat

  return 1


-- |
--
-- 
wait :: MVar C.DebugContextData -> IO ()
wait mvarDat = readStdin BSL.empty
  where
    readStdin buf = BSL.hGet stdin 1 >>= withC buf
    
    withC buf c
      | c == BSL.empty = unexpectedEOF
      | otherwise      = withBuf $ BSL.append buf c
    
    withBuf buf = case parse parser "readContentLengthParser" (lbs2str buf) of
        Left _    -> readStdin buf
        Right len -> BSL.hGet stdin len >>= withCnt buf

    withCnt buf cnt
      | cnt == BSL.empty = unexpectedEOF
      | otherwise        = do
        C.handleRequest mvarDat buf cnt
        wait mvarDat

    parser = do
      string _CONTENT_LENGTH
      len <- manyTill digit (string _TWO_CRLF)
      return . read $ len

    unexpectedEOF = do
      L.criticalM _LOG_NAME "unexpected EOF from stdin."
      return ()



-- |
--
sendResponse :: BSL.ByteString -> IO ()
sendResponse str = do
  BSL.hPut stdout $ str2lbs $ _CONTENT_LENGTH ++ (show (BSL.length str))
  BSL.hPut stdout $ str2lbs _TWO_CRLF
  BSL.hPut stdout str
  hFlush stdout