{-# LANGUAGE OverloadedStrings #-}
module System.Cached.JSON (
getCachedJSON,
getCachedJSONQuery,
lookupKey
)
where
import Control.Monad
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Time.Clock (diffUTCTime, getCurrentTime, NominalDiffTime)
import Network.HTTP.Query
import System.Directory
import System.Environment.XDG.BaseDir
import System.FilePath
getCachedJSON :: (FromJSON a, ToJSON a)
=> String
-> FilePath
-> String
-> NominalDiffTime
-> IO a
getCachedJSON :: String -> String -> String -> NominalDiffTime -> IO a
getCachedJSON String
prog String
jsonfile String
url =
String -> String -> IO a -> NominalDiffTime -> IO a
forall a.
(FromJSON a, ToJSON a) =>
String -> String -> IO a -> NominalDiffTime -> IO a
getCachedJSONQuery String
prog String
jsonfile (String -> Query -> IO a
forall (m :: * -> *) a.
(MonadIO m, FromJSON a) =>
String -> Query -> m a
webAPIQuery String
url [])
getCachedJSONQuery :: (FromJSON a, ToJSON a)
=> String
-> FilePath
-> IO a
-> NominalDiffTime
-> IO a
getCachedJSONQuery :: String -> String -> IO a -> NominalDiffTime -> IO a
getCachedJSONQuery String
prog String
jsonfile IO a
webquery NominalDiffTime
minutes = do
String
file <- String -> String -> IO String
getUserCacheFile String
prog String
jsonfile
Bool
exists <- String -> IO Bool
doesFileExist String
file
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
exists (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Creating " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
file String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" ..."
Bool -> String -> IO ()
createDirectoryIfMissing Bool
True (String -> String
takeDirectory String
file)
Bool
recent <- do
if Bool
exists then do
UTCTime
ts <- String -> IO UTCTime
getModificationTime String
file
UTCTime
t <- IO UTCTime
getCurrentTime
Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ UTCTime -> UTCTime -> NominalDiffTime
diffUTCTime UTCTime
t UTCTime
ts NominalDiffTime -> NominalDiffTime -> Bool
forall a. Ord a => a -> a -> Bool
< (NominalDiffTime
minutes NominalDiffTime -> NominalDiffTime -> NominalDiffTime
forall a. Num a => a -> a -> a
* NominalDiffTime
60)
else Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
if Bool
recent
then do
Either String a
eObj <- ByteString -> Either String a
forall a. FromJSON a => ByteString -> Either String a
eitherDecode (ByteString -> Either String a)
-> IO ByteString -> IO (Either String a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO ByteString
B.readFile String
file
case Either String a
eObj of
Left String
err -> String -> IO a
forall a. HasCallStack => String -> a
error String
err
Right a
obj -> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
obj
else do
a
obj <- IO a
webquery
String -> ByteString -> IO ()
B.writeFile String
file (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ a -> ByteString
forall a. ToJSON a => a -> ByteString
encode a
obj
a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
obj