{-# LANGUAGE LambdaCase #-} module Main ( main ) where import Control.Monad (when) import Data.Semigroup ((<>)) import Data.Version (showVersion) import System.Exit (die) import System.IO (IOMode(ReadMode), hGetLine, hPrint, stderr, withFile) import Options.Applicative ( Parser , (<**>) , (<|>) , auto , execParser , flag' , fullDesc , header , help , helper , info , long , metavar , option , optional , short , strOption , switch ) import System.Directory (createDirectoryIfMissing) import System.Environment.XDG.BaseDir (getUserConfigDir, getUserConfigFile) import qualified Web.OpenWeatherMap.Client as Client import Paths_openweathermap (version) -- from cabal import Print (printCurrectWeather, printForecastWeather) appName :: String appName = "openweathermap" parseLocation :: Parser Client.Location parseLocation = byName <|> byCoord where byName = Client.Name <$> strOption (long "city" <> short 'c' <> metavar "CITY" <> help "City name") byCoord = Client.Coord <$> option auto (long "lat" <> metavar "NUM" <> help "Latitude in decimal degrees") <*> option auto (long "lon" <> metavar "NUM" <> help "Longitude in decimal degrees") data ApiKey = ApiKeyFile FilePath | ApiKey String parseApiKey :: Parser ApiKey parseApiKey = fromFile <|> inCmdLine where fromFile = ApiKeyFile <$> strOption (long "api-key-file" <> short 'K' <> metavar "APIKEYFILE" <> help "Read API key from this file") inCmdLine = ApiKey <$> strOption (long "api-key" <> short 'k' <> metavar "APIKEY" <> help "API key") data Weather = Current | Forecast parseWeather :: Parser Weather parseWeather = flag' Current (long "current" <> short 'n' <> help "current weather (default)") <|> flag' Forecast (long "forecast" <> short 'f' <> help "forecast weather") <|> pure Current data Config = Config { apikey :: Maybe ApiKey , location :: Client.Location , weather :: Weather , debug :: Bool } parseConfig :: Parser Config parseConfig = Config <$> optional parseApiKey <*> parseLocation <*> parseWeather <*> switch (long "debug" <> short 'd' <> help "Enable debug") getApiKey :: Maybe ApiKey -> IO String getApiKey (Just (ApiKey key)) = return key getApiKey (Just (ApiKeyFile f)) = withFile f ReadMode hGetLine getApiKey Nothing = do createDirectoryIfMissing True =<< getUserConfigDir appName getUserConfigFile appName "key" >>= getApiKey . Just . ApiKeyFile run :: Config -> IO () run cfg = do appid <- getApiKey . apikey $ cfg case weather cfg of Current -> Client.getWeather appid (location cfg) >>= \case Left err -> die $ show err Right cw -> do when (debug cfg) $ hPrint stderr cw printCurrectWeather cw Forecast -> Client.getForecast appid (location cfg) >>= \case Left err -> die $ show err Right fw -> do when (debug cfg) $ hPrint stderr fw printForecastWeather fw main :: IO () main = run =<< execParser opts where opts = info (parseConfig <**> helper) (fullDesc <> header desc) desc = "openweathermap " ++ showVersion version ++ " - command-line client for https://openweathermap.org/api"