{-# LANGUAGE OverloadedStrings #-}
module AWS.EventBridge.Minutes where
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import Text.Megaparsec
import Text.Megaparsec.Char
import Text.Megaparsec.Char.Lexer
import Data.Void
type Parser = Parsec Void Text
data MinutesExprT
= AllMinutes
| AtMinute Int
| RangeMinute Int Int
| StepMinute Int Int
| UnionMinute MinutesExprT MinutesExprT
deriving (Eq, Show)
isValidMinute :: Int -> Bool
isValidMinute m = 0 <= m && m <= 59
evaluateMinuteT :: MinutesExprT -> Either String [Int]
evaluateMinuteT mExpr = fmap S.toAscList (go mExpr >>= ensureBounds . S.fromList)
where
ensureBounds xs =
if all isValidMinute xs
then Right xs
else Left "minute out of bounds: expected 0..59"
go AllMinutes = Right [0..59]
go (AtMinute m)
| isValidMinute m = Right [m]
| otherwise = Left ("invalid minute: " ++ show m ++ " (expected 0..59)")
go (RangeMinute a b)
| isValidMinute a && isValidMinute b && a <= b = Right [a..b]
| not (isValidMinute a) = Left ("invalid range start: " ++ show a ++ " (expected 0..59)")
| not (isValidMinute b) = Left ("invalid range end: " ++ show b ++ " (expected 0..59)")
| otherwise = Left ("invalid range: start " ++ show a ++ " > end " ++ show b)
go (StepMinute m s)
| not (isValidMinute m) = Left ("invalid step start: " ++ show m ++ " (expected 0..59)")
| s >= 1 = Right (takeWhile (<= 59) [m, m + s ..])
| otherwise = Left ("invalid step: " ++ show s ++ " (expected >= 1)")
go (UnionMinute a b) = (++) <$> go a <*> go b
parseMinutes :: Parser MinutesExprT
parseMinutes = choice
[ try parseUnionMinute
, try parseStepMinute
, try parseRangeMinute
, try parseAtMinute
, parseAllMinute
]
parseAllMinute :: Parser MinutesExprT
parseAllMinute = char '*' >> pure AllMinutes
parseAtMinute :: Parser MinutesExprT
parseAtMinute = AtMinute <$> decimal
parseRangeMinute :: Parser MinutesExprT
parseRangeMinute = do
start <- decimal
_ <- char '-'
RangeMinute start <$> decimal
parseStepMinute :: Parser MinutesExprT
parseStepMinute = do
start <- decimal
_ <- char '/'
StepMinute start <$> decimal
parseUnionMinute :: Parser MinutesExprT
parseUnionMinute = do
s <- choice [try parseStepMinute, try parseRangeMinute, try parseAtMinute, parseAllMinute]
_ <- char ','
e <- choice [try parseUnionMinute, try parseStepMinute, try parseRangeMinute, try parseAtMinute, parseAllMinute]
eof
pure $ UnionMinute s e
parseMinutesText :: Text -> Either String MinutesExprT
parseMinutesText input =
case parse parseMinutes "minutes" (T.strip input) of
Left err -> Left (errorBundlePretty err)
Right expr -> Right expr