{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TemplateHaskell #-} module Network.Yak ( emit , emitSome , Fetch , fetch , fetch' -- * Messages , T.build , T.buildPrefix , T.vacant , T.castMsg , (T.<:>) -- * Types , module Network.Yak.Types ) where import Control.Applicative import Data.Attoparsec.ByteString.Char8 import Data.ByteString.Char8 (ByteString) import Data.Maybe import Data.Proxy import Data.Text.Encoding import GHC.TypeLits import Network.Yak.Types hiding (build, buildPrefix, vacant, castMsg, (<:>)) import qualified Network.Yak.Types as T import qualified Data.ByteString.Char8 as B -- | Encode an IRC message to a 'ByteString', ready for the network emit :: forall c p. (Parameter (PList p), KnownSymbol c) => Msg c p -> ByteString emit Msg{..} = fromMaybe "" (flip mappend " " . mappend ":" . rpfx <$> msgPrefix) <> (B.pack $ symbolVal (Proxy @c)) <> " " <> render msgParams <> "\n" where rpfx (PrefixServer x) = render x rpfx (PrefixUser h) = render h -- | Encode existentially quantified message. emitSome :: SomeMsg -> ByteString emitSome (SomeMsg r) = emit r class Fetch a where -- | Decode an IRC message from a 'ByteString' into a 'Msg' or a coproduct -- thereof. This function is return type polymorphic and will pick a parser -- that fits the requested type, which is determined either by type -- inference or can be picked by explicit type annotation. fetch :: ByteString -> Maybe a instance (Parameter (PList p), KnownSymbol c) => Fetch (Msg c p) where fetch = either (const Nothing) Just . parseOnly fetch' instance (Fetch a, Fetch b) => Fetch (Either a b) where fetch x = case fetch @a x of Just l -> Just (Left l) Nothing -> Right <$> fetch x -- | Like 'fetch' but offers the underlying attoparsec Parser. This can be used -- for e.g. the construction of ad-hoc sum types catching multiple message -- types. fetch' :: forall c p. (Parameter (PList p), KnownSymbol c) => Parser (Msg c p) fetch' = Msg <$> optional (char ':' *> pfx) <*> (skipSpace *> cmd *> skipSpace *> seize) where cmd = string . B.pack . symbolVal $ Proxy @c pfx = (PrefixUser <$> seize) <|> (PrefixServer <$> srv) srv = decodeUtf8 <$> takeTill isSpace