packages feed

sandwich-0.3.1.0: src/Test/Sandwich/Formatters/TerminalUI/DebugSocket.hs

module Test.Sandwich.Formatters.TerminalUI.DebugSocket (
  debugSocketServer
  ) where

import Control.Concurrent.STM
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.Text as T
import Network.Socket
import Network.Socket.ByteString (sendAll)
import System.Directory (removeFile)
import Test.Sandwich.ManagedAsync
import UnliftIO.Exception


-- | Debug socket server that accepts connections and broadcasts events.
-- Connect with @nc -U \<path\>@ to receive line-oriented debug events.
debugSocketServer :: T.Text -> FilePath -> TChan ByteString -> IO ()
debugSocketServer runId socketPath chan = do
  -- Clean up any existing socket file
  removeFile socketPath `catch` \(_ :: IOError) -> return ()

  bracket (socket AF_UNIX Stream defaultProtocol) close $ \sock -> do
    bind sock (SockAddrUnix socketPath)
    listen sock 5
    forever $ do
      (conn, _) <- accept sock
      -- Spawn a thread to handle this connection
      void $ managedAsync runId "tui-debug-connection" $ handleConnection conn
  where
    handleConnection conn = do
      -- Duplicate the channel so this client gets its own read position
      chan' <- atomically $ dupTChan chan
      -- Send events until error (client disconnect)
      handle (\(_ :: IOError) -> close conn) $ forever $ do
        msg <- atomically $ readTChan chan'
        sendAll conn msg