packages feed

cachix-1.7.5: src/Cachix/Daemon/Protocol.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Cachix.Daemon.Protocol
  ( ClientMessage (..),
    DaemonMessage (..),
    DaemonExitStatus (..),
    PushRequestId,
    newPushRequestId,
    PushRequest (..),
    newMessage,
    splitMessages,
  )
where

import Data.Aeson qualified as Aeson
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS
import Data.UUID (UUID)
import Data.UUID.V4 qualified as UUID
import Protolude

-- | JSON messages that the client can send to the daemon
data ClientMessage
  = ClientPushRequest !PushRequest
  | ClientStop
  | ClientPing
  deriving stock (Eq, Generic, Show)
  deriving anyclass (Aeson.FromJSON, Aeson.ToJSON)

-- | JSON messages that the daemon can send to the client
data DaemonMessage
  = DaemonPong
  | DaemonExit !DaemonExitStatus
  deriving stock (Eq, Generic, Show)
  deriving anyclass (Aeson.FromJSON, Aeson.ToJSON)

data DaemonExitStatus = DaemonExitStatus
  { exitCode :: !Int,
    exitMessage :: !(Maybe Text)
  }
  deriving stock (Eq, Generic, Show)
  deriving anyclass (Aeson.FromJSON, Aeson.ToJSON)

newtype PushRequestId = PushRequestId UUID
  deriving stock (Generic)
  deriving newtype (Eq, Ord, Show, Aeson.FromJSON, Aeson.ToJSON, Hashable)

newPushRequestId :: (MonadIO m) => m PushRequestId
newPushRequestId = liftIO $ PushRequestId <$> UUID.nextRandom

-- | A request for the daemon to push store paths to a binary cache
data PushRequest = PushRequest
  { storePaths :: [FilePath]
  }
  deriving stock (Eq, Generic, Show)
  deriving anyclass (Aeson.FromJSON, Aeson.ToJSON)

newMessage :: (Aeson.ToJSON a) => a -> LBS.ByteString
newMessage msg =
  Aeson.encode msg `LBS.snoc` fromIntegral (fromEnum '\n')

splitMessages :: ByteString -> ([ByteString], ByteString)
splitMessages = go []
  where
    go acc bs =
      case BS.break (== _n) bs of
        (line, "") -> (reverse acc, line)
        (line, "\n") -> go (line `cons` acc) mempty
        (line, rest) ->
          go (line `cons` acc) (BS.drop 1 rest)

    cons "" xs = xs
    cons x xs = x : xs

    _n = fromIntegral (fromEnum '\n')