apelsin-1.0: src/ClanFetcher.hs
module ClanFetcher(
Clan(..), TagExpr, matchTagExpr, getClanList, clanListFromCache
) where
import Prelude as P hiding (catch)
import Control.Exception
import Control.Applicative
import Control.Monad
import Data.ByteString.Char8 as B
import Data.ByteString.Lazy.Char8 as L
import Data.Char (intToDigit, toLower)
import Data.Maybe
import Network.HTTP
import Network.URI
import Network.Tremulous.NameInsensitive
import Constants
data TagExpr =
TagPrefix !B.ByteString
| TagSuffix !B.ByteString
| TagInfix !B.ByteString
| TagContained !B.ByteString !B.ByteString
| TagError
data Clan = Clan {
clanid :: !B.ByteString
, name
, tag :: !TI
, website
, irc :: !B.ByteString
, tagexpr :: !TagExpr
}
mkTagExpr :: B.ByteString -> TagExpr
mkTagExpr str
| B.length str > 0 = case x of
'<' -> TagPrefix xs
'>' -> TagSuffix xs
'^' -> TagInfix xs
'%' -> let (a, b) = B.break (=='%') xs in TagContained a (B.drop 1 b)
_ -> TagError
| otherwise = TagError
where (x, xs) = (B.head str, B.map toLower (B.tail str))
matchTagExpr :: TagExpr -> TI -> Bool
matchTagExpr expr raw = case expr of
TagPrefix xs -> xs `B.isPrefixOf` str
TagSuffix xs -> xs `B.isSuffixOf` str
TagInfix xs -> xs `B.isInfixOf` str
TagContained xs ys -> xs `B.isPrefixOf` str && ys `B.isSuffixOf` str
TagError -> False
where str = cleanedCase raw
rawToClan :: L.ByteString -> Maybe [Clan]
rawToClan = mapM (f . B.split '\t' . lazyToStrict) . P.filter (not . L.null) . L.split '\n' where
f [clanid, rname, rtag, website, irc, rexpr, _] =
Just Clan { name = mkAlphaNum rname
, tag = mk rtag
, tagexpr = mkTagExpr rexpr
, .. }
f _ = Nothing
getClanList :: String -> IO (Maybe [Clan])
getClanList url = do
cont <- get url
case cont of
Right _ -> return Nothing
Left raw -> do
file <- inCacheDir "clans"
let clans = rawToClan raw
when (isJust clans) $
L.writeFile file raw
return clans
clanListFromCache :: IO [Clan]
clanListFromCache = handle err $ do
file <- inCacheDir "clans"
fromMaybe [] . rawToClan <$> L.readFile file
where
err (_::IOError) = return []
lazyToStrict :: L.ByteString -> B.ByteString
lazyToStrict = B.concat . toChunks
-- I blame this on buggy Network.HTTP
get :: HStream ty => String -> IO (Either ty String)
get url = case parseURI url of
Nothing -> return $ Right "Error in URL"
Just uri -> do
resp <- catch (getRight `fmap` simpleHTTP (mkRequest GET uri)) err
return $ case resp of
Left msg -> Right $ show msg
Right (Response (2,0,0) _ _ body) -> Left body
Right (Response code reason _ _) -> Right $ showRspCode code ++ " " ++ reason
where
showRspCode (a,b,c) = P.map intToDigit [a,b,c]
getRight (Right a) = Right a
getRight (Left _) = error ""
err :: IOException -> IO (Either String a )
err = return . Left . show