module Currency.OpenExchangeRates (fetchRates) where import Control.Error (readZ, syncIO, fmapLT, hoistEither, throwT, EitherT) import Data.String (fromString) import qualified Data.Map as Map import qualified Data.Text as T import Data.Aeson ((.:)) import qualified Data.Aeson as Aeson import qualified Network.HTTP as HTTP import qualified Network.Stream as HTTP import qualified Network.URI as URI import Currency import Currency.Rates newtype OERs = OERs (Rates Currency Double) instance Aeson.FromJSON OERs where parseJSON (Aeson.Object o) = do ref <- readZ =<< (o .: (T.pack "base")) rs <- fmap Map.toList (o .: (T.pack "rates")) let rs' = map (\(k,v) -> (fromString k, v)) rs return $ OERs $ Rates ref (Map.fromList rs') parseJSON _ = fail "OpenExchangeRates data is an object." -- | Fetch exchange rates from OpenExchangeRates.org fetchRates :: String -- ^ AppID -> EitherT HTTP.ConnError IO (Rates Currency Double) fetchRates appid = do resp <- hoistEither =<< (tryHTTP $ HTTP.simpleHTTP req) case resp of (HTTP.Response { HTTP.rspCode = (2,0,0), HTTP.rspBody = body }) -> do OERs rs <- fmapLT HTTP.ErrorMisc $ hoistEither (Aeson.eitherDecode body) return rs _ -> throwT (HTTP.ErrorMisc "Bad HTTP response code.") where tryHTTP = fmapLT (HTTP.ErrorMisc . show) . syncIO req = HTTP.mkRequest HTTP.GET uri Just uri = URI.parseURI $ "http://openexchangerates.org/api/latest.json?app_id=" ++ appid