packages feed

daemons-0.1.2: examples/Name.hs

{-# LANGUAGE DeriveGeneric, OverloadedStrings, MultiParamTypeClasses, FunctionalDependencies #-}

module Main where

import Control.Concurrent.MVar ( MVar, newMVar, modifyMVar )
import Data.ByteString.Char8 ( ByteString, unpack )
import Data.Default ( def )
import Data.Serialize ( Serialize )
import Data.String ( fromString )
import qualified Data.Map as M
import GHC.Generics
import System.Environment ( getArgs )
import System.Daemon

data CommandV0 = Register ByteString Port
               | WhereIs ByteString
                 deriving ( Generic, Show )

instance Serialize CommandV0

data Command = CommandV0 CommandV0
               deriving ( Generic, Show )

instance Serialize Command

data ResponseV0 = Ok
                | NotFound ByteString
                | AtPort ByteString Port
                  deriving ( Generic, Show )

instance Serialize ResponseV0

data Response = ResponseV0 ResponseV0
                deriving ( Generic, Show )

instance Serialize Response

type Registry = M.Map ByteString Port

class VersionOf a b | a -> b where
    toLatest :: a -> b
    fromLatest :: b -> a

instance VersionOf Command CommandV0 where
    toLatest (CommandV0 v0) = v0
    fromLatest v0 = CommandV0 v0

instance VersionOf Response ResponseV0 where
    toLatest (ResponseV0 v0) = v0
    fromLatest v0 = ResponseV0 v0

namePort :: Port
namePort = 4370

handleCommand :: MVar Registry -> CommandV0 -> IO ResponseV0
handleCommand registryVar cmd = modifyMVar registryVar $ \registry -> return $
    case cmd of
      WhereIs name -> ( registry
                      , maybe (NotFound name) (AtPort name) (M.lookup name registry) )
      Register name port -> ( M.insert name port registry
                            , Ok )

wrapVersion :: (CommandV0 -> IO ResponseV0) -> Command -> IO Response
wrapVersion f cmd = do
  rsp <- f (toLatest cmd)
  return (fromLatest rsp)

main :: IO ()
main = do
    registryVar <- newMVar M.empty
    let options = def { daemonPort = namePort }
    ensureDaemonRunning "name" options (wrapVersion (handleCommand registryVar))
    args <- getArgs
    let args' = map fromString args
    res <- case args' of
      ["where-is", key] ->
          runClient "localhost" namePort (CommandV0 (WhereIs key))
      ["register", name, port] ->
          let portNum = read (unpack port) in
          runClient "localhost" namePort (CommandV0 (Register name portNum))
      _ ->
          error "invalid command"
    print (fmap toLatest (res :: Maybe Response))