{-# LANGUAGE OverloadedStrings #-} module AWS.EventBridge.Years where import qualified Data.Set as S import Data.Text (Text) import qualified Data.Text as T import Data.Void (Void) import Text.Megaparsec (Parsec, choice, eof, errorBundlePretty, parse, try) import Text.Megaparsec.Char (char) import Text.Megaparsec.Char.Lexer (decimal) type Parser = Parsec Void Text data YearsExprT = AllYears | AtYear Int | RangeYear Int Int | StepYear Int Int | UnionYear YearsExprT YearsExprT deriving (Eq, Show) -- AWS EventBridge supports calendar years 1970..2199 inclusive. isValidYear :: Int -> Bool isValidYear y = 1970 <= y && y <= 2199 evaluateYearT :: YearsExprT -> Either String [Int] evaluateYearT expr = fmap S.toAscList (go expr >>= ensureBounds . S.fromList) where ensureBounds xs = if all isValidYear xs then Right xs else Left "year out of bounds: expected 1970..2199" go AllYears = Right [1970..2199] go (AtYear y) | isValidYear y = Right [y] | otherwise = Left ("invalid year: " ++ show y ++ " (expected 1970..2199)") go (RangeYear a b) | isValidYear a && isValidYear b && a <= b = Right [a..b] | not (isValidYear a) = Left ("invalid range start: " ++ show a ++ " (expected 1970..2199)") | not (isValidYear b) = Left ("invalid range end: " ++ show b ++ " (expected 1970..2199)") | otherwise = Left ("invalid range: start " ++ show a ++ " > end " ++ show b) go (StepYear start step) | not (isValidYear start) = Left ("invalid step start: " ++ show start ++ " (expected 1970..2199)") | step >= 1 = Right (takeWhile (<= 2199) [start, start + step ..]) | otherwise = Left ("invalid step: " ++ show step ++ " (expected >= 1)") go (UnionYear a b) = (++) <$> go a <*> go b parseYears :: Parser YearsExprT parseYears = choice [ try parseUnionYear , try parseStepYear , try parseRangeYear , try parseAtYear , parseAllYear ] parseAllYear :: Parser YearsExprT parseAllYear = char '*' >> pure AllYears parseAtYear :: Parser YearsExprT parseAtYear = AtYear <$> decimal parseRangeYear :: Parser YearsExprT parseRangeYear = do start <- decimal _ <- char '-' RangeYear start <$> decimal parseStepYear :: Parser YearsExprT parseStepYear = do start <- decimal _ <- char '/' StepYear start <$> decimal parseUnionYear :: Parser YearsExprT parseUnionYear = do s <- choice [try parseStepYear, try parseRangeYear, try parseAtYear, parseAllYear] _ <- char ',' e <- choice [try parseUnionYear, try parseStepYear, try parseRangeYear, try parseAtYear, parseAllYear] eof pure (UnionYear s e) parseYearsText :: Text -> Either String YearsExprT parseYearsText input = case parse parseYears "years" (T.strip input) of Left err -> Left (errorBundlePretty err) Right expr -> Right expr