{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-} module Propellor.Property.Scheduled ( period , periodParse , Recurrance(..) , WeekDay , MonthDay , YearDay ) where import Propellor.Base import Propellor.Types.Core import Utility.Scheduled import Data.Time.Clock import Data.Time.LocalTime import qualified Data.Map as M -- | Makes a Property only be checked every so often. -- -- This uses the description of the Property to keep track of when it was -- last run. period :: (IsProp (Property i)) => Property i -> Recurrance -> Property i period prop recurrance = flip describe desc $ adjustPropertySatisfy prop $ \satisfy -> do lasttime <- liftIO $ getLastChecked (getDesc prop) nexttime <- liftIO $ fmap startTime <$> nextTime schedule lasttime t <- liftIO localNow if Just t >= nexttime then do r <- satisfy liftIO $ setLastChecked t (getDesc prop) return r else noChange where schedule = Schedule recurrance AnyTime desc = getDesc prop ++ " (period " ++ fromRecurrance recurrance ++ ")" -- | Like period, but parse a human-friendly string. periodParse :: (IsProp (Property i)) => Property i -> String -> Property i periodParse prop s = case toRecurrance s of Just recurrance -> period prop recurrance Nothing -> adjustPropertySatisfy prop $ \_ -> do liftIO $ warningMessage $ "failed periodParse: " ++ s noChange lastCheckedFile :: FilePath lastCheckedFile = localdir ".lastchecked" getLastChecked :: Desc -> IO (Maybe LocalTime) getLastChecked desc = M.lookup desc <$> readLastChecked localNow :: IO LocalTime localNow = do now <- getCurrentTime tz <- getTimeZone now return $ utcToLocalTime tz now setLastChecked :: LocalTime -> Desc -> IO () setLastChecked time desc = do m <- readLastChecked writeLastChecked (M.insert desc time m) readLastChecked :: IO (M.Map Desc LocalTime) readLastChecked = fromMaybe M.empty <$> catchDefaultIO Nothing go where go = readish <$> readFileStrict lastCheckedFile writeLastChecked :: M.Map Desc LocalTime -> IO () writeLastChecked = writeFile lastCheckedFile . show