kevin-0.1.1: Kevin/Damn/Packet.hs
module Kevin.Damn.Packet (
Packet(..),
parsePacket,
parsePrivclasses,
subPacket,
fixLoginPacket,
okay,
getArg,
splitOn,
readable
) where
import Kevin.Base (KevinException(..), Privclass)
import Control.Exception (throw)
import qualified Data.Text as T
import Data.Attoparsec.Text
import Data.Char
import Control.Applicative (many, (<$>))
import Control.Monad (liftM2)
import Control.Monad.Fix
import Data.Maybe
data Packet = Packet { command :: T.Text
, parameter :: Maybe T.Text
, args :: [(T.Text,T.Text)]
, body :: Maybe T.Text
} deriving (Show)
parseCommand :: Parser T.Text
parseCommand = takeWhile1 (not . isSpace)
parseParam :: Parser (Maybe T.Text)
parseParam = do
char ' '
parm <- takeWhile1 (not . isSpace)
return $ Just parm
parseArgs :: Parser [(T.Text,T.Text)]
parseArgs = many $ do
char '\n'
c <- takeTill (=='=')
char '='
r <- takeTill (=='\n')
return (c,r)
parseHead :: Parser Packet
parseHead = do
c <- parseCommand
p <- option Nothing parseParam
a <- parseArgs
return $ Packet c p a Nothing
parsePacket :: T.Text -> Packet
parsePacket pack = let (top,b) = T.breakOn "\n\n" pack in case (\x -> (x { body = if T.null b then Nothing else Just $ T.drop 2 b }) :: Packet) <$> parseOnly parseHead top of
Left _ -> throw ParseFailure
Right pkt -> pkt
getResult :: Either String a -> a
getResult (Right x) = x
getResult (Left _) = error "getResult"
fixLoginPacket :: Packet -> Packet
fixLoginPacket pkt = if command pkt == "login"
then pkt { args = args pkt ++ getResult (parseOnly parseArgs . T.cons '\n' . fromJust . body $ pkt), body = Nothing }
else pkt
subPacket :: Packet -> Maybe Packet
subPacket = (parsePacket <$>) . body
okay :: Packet -> Bool
okay (Packet _ _ a _) = let e = lookup "e" a in isNothing e || e == Just "ok"
getArg :: T.Text -> Packet -> T.Text
getArg b p = fromMaybe "" $ lookup b (args p)
parsePrivclasses :: T.Text -> [Privclass]
parsePrivclasses = map (liftM2 (,) (!! 1) (read . T.unpack . (!! 0)) . T.splitOn ":") . filter (not . T.null) . T.splitOn "\n"
splitOn :: T.Text -> T.Text -> [T.Text]
splitOn delim = fix (\rec s -> let (f,l) = T.breakOn delim s in if T.null l then [f] else f:rec (T.drop (T.length delim) l))
readable :: Packet -> T.Text
readable (Packet cmd param arg bod) = cmd +++ maybe "" (' ' `T.cons`) param +++ formattedArgs arg +++ maybe "" ("\n\n" `T.append`) bod +++ "\n\0"
where
(+++) = T.append
formattedArgs [] = ""
formattedArgs q = T.append "\n" . T.intercalate "\n" . map (uncurry (\x y -> x +++ "=" +++ y)) $ q