aws-eventbridge-cron 0.1.0.0 → 0.1.0.1
raw patch · 5 files changed
+93/−6 lines, 5 filesPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
API changes (from Hackage documentation)
+ AWS.EventBridge.Cron: CronSchedule :: ScheduleKind
+ AWS.EventBridge.Cron: OneTimeSchedule :: ScheduleKind
+ AWS.EventBridge.Cron: RateSchedule :: ScheduleKind
+ AWS.EventBridge.Cron: data ScheduleKind
+ AWS.EventBridge.Cron: instance GHC.Classes.Eq AWS.EventBridge.Cron.ScheduleKind
+ AWS.EventBridge.Cron: instance GHC.Classes.Ord AWS.EventBridge.Cron.ScheduleKind
+ AWS.EventBridge.Cron: instance GHC.Show.Show AWS.EventBridge.Cron.ScheduleKind
+ AWS.EventBridge.Cron: isRecurring :: CronExprT -> Bool
+ AWS.EventBridge.Cron: scheduleKind :: CronExprT -> ScheduleKind
Files
- CHANGELOG.md +6/−2
- README.md +12/−0
- aws-eventbridge-cron.cabal +1/−1
- src/AWS/EventBridge/Cron.hs +25/−3
- test/AWS/EventBridge/CronSpec.hs +49/−0
CHANGELOG.md view
@@ -5,10 +5,14 @@ The format is based on Keep a Changelog and this project adheres to Semantic Versioning. +## [0.1.0.1] - 2025-11-17+### Added+- `ScheduleKind`, `scheduleKind`, and `isRecurring` helpers for classifying parsed schedules without re-parsing downstream.+- QuickCheck properties covering the new helpers to ensure they align with parsed expression variants.+- README documentation demonstrating schedule introspection and clarifying the helper API.+ ## [0.1.0.0] - 2025-11-16 ### Added - Initial `AWS.EventBridge.Cron` module for parsing and scheduling AWS Scheduler cron expressions. - Support for `rate(...)` and `at(...)` expressions with validation, evaluation, and round-trip tests. - Property-based and unit tests across all cron fields plus helper utilities.-- Project metadata, README, LICENSE, and Haddock documentation suitable for Hackage distribution.-- GitHub Actions workflows for CI and release automation, including Hackage candidate/publish steps.
README.md view
@@ -13,6 +13,9 @@ weekday ranges, and nth-weekday modifiers (`2#1`). - `rate(...)` and `at(...)` expressions share the same API, so callers do not need to branch on expression variants.+- Schedule introspection helpers: `scheduleKind` returns a `ScheduleKind`, and+ `isRecurring` distinguishes recurring (`cron`/`rate`) expressions from+ `at(...)` one-time schedules. - Extensive property-based test suite that mirrors the behaviour documented by AWS. @@ -58,6 +61,15 @@ atExample = do expr <- parseCronText "at(2025-11-16T09:30:00)" nextRunTimes expr base 5++-- Introspect the parsed expression without re-parsing downstream.+kindExample :: Either String ScheduleKind+kindExample = scheduleKind <$> parseCronText "rate(5 minutes)"+-- Right RateSchedule++isRecurringExample :: Either String Bool+isRecurringExample = isRecurring <$> parseCronText "at(2025-11-16T09:30:00)"+-- Right False ``` ### Error Reporting
aws-eventbridge-cron.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: aws-eventbridge-cron-version: 0.1.0.0+version: 0.1.0.1 synopsis: AWS EventBridge cron, rate, and one-time parser with scheduler description: Parse AWS EventBridge cron, rate, and one-time expressions and compute
src/AWS/EventBridge/Cron.hs view
@@ -6,6 +6,9 @@ -- support for cron, rate, and one-time ("at") expressions. module AWS.EventBridge.Cron ( CronExprT+ , ScheduleKind(..)+ , scheduleKind+ , isRecurring , parseCronText , nextRunTimes ) where@@ -29,9 +32,8 @@ -- | EventBridge scheduling expression. ----- Constructors mirror the three families of EventBridge schedules. The structure is--- exposed to allow pattern matching by advanced users, but most callers should rely on--- the parser and evaluator helpers provided by this module.+-- The concrete representation is intentionally opaque; use 'scheduleKind' to detect the+-- backing schedule family and 'nextRunTimes' to evaluate upcoming occurrences. data CronExprT = CronExpr { minutes :: MinutesExprT@@ -44,6 +46,26 @@ | RateExpr RateExprT | OneTimeExpr OneTimeExprT deriving (Eq, Show)++-- | Classification of an EventBridge scheduling expression.+data ScheduleKind+ = CronSchedule+ | RateSchedule+ | OneTimeSchedule+ deriving (Eq, Ord, Show)++-- | Determine which family of expression a parsed value belongs to.+scheduleKind :: CronExprT -> ScheduleKind+scheduleKind CronExpr{} = CronSchedule+scheduleKind RateExpr{} = RateSchedule+scheduleKind OneTimeExpr{} = OneTimeSchedule++-- | True when the schedule produces multiple occurrences (cron or rate).+isRecurring :: CronExprT -> Bool+isRecurring expr =+ case scheduleKind expr of+ OneTimeSchedule -> False+ _ -> True type Parser = Parsec Void Text
test/AWS/EventBridge/CronSpec.hs view
@@ -90,11 +90,60 @@ propertyTests :: TestTree propertyTests = testGroup "properties" [ QC.testProperty "rate schedule monotonic" propRateMonotonic+ , QC.testProperty "schedule kind round-trips for cron" propScheduleKindCron+ , QC.testProperty "schedule kind round-trips for rate" propScheduleKindRate+ , QC.testProperty "schedule kind identifies one-time" propScheduleKindOneTime+ , QC.testProperty "isRecurring agrees with schedule kind" propIsRecurring , QC.testProperty "cron returns at most requested" propCronLimit , QC.testProperty "cron schedule monotonic and >= base" propCronMonotonic , QC.testProperty "cron with '?' dom relies on day-of-week" propCronDomQuestionUsesDow , QC.testProperty "cron with '?' dow relies on day-of-month" propCronDowQuestionUsesDom ]++propScheduleKindCron :: QC.Property+propScheduleKindCron =+ let exprText = "cron(0 0 ? NOV MON 2025)"+ in case parseCronText exprText of+ Left err -> QC.counterexample ("cron parse failed: " <> err) False+ Right expr ->+ QC.counterexample (show (scheduleKind expr)) (scheduleKind expr QC.=== CronSchedule)++propScheduleKindRate :: QC.Property+propScheduleKindRate =+ let exprText = "rate(15 minutes)"+ in case parseCronText exprText of+ Left err -> QC.counterexample ("rate parse failed: " <> err) False+ Right expr ->+ QC.counterexample (show (scheduleKind expr)) (scheduleKind expr QC.=== RateSchedule)++propScheduleKindOneTime :: QC.Property+propScheduleKindOneTime =+ let exprText = "at(2025-11-16T09:30:00)"+ in case parseCronText exprText of+ Left err -> QC.counterexample ("at parse failed: " <> err) False+ Right expr ->+ QC.counterexample (show (scheduleKind expr)) (scheduleKind expr QC.=== OneTimeSchedule)++propIsRecurring :: QC.Property+propIsRecurring =+ QC.conjoin+ [ QC.counterexample "cron should be recurring" cronRecurring+ , QC.counterexample "rate should be recurring" rateRecurring+ , QC.counterexample "one-time should not be recurring" oneTimeNonRecurring+ ]+ where+ cronRecurring =+ case parseCronText "cron(0 12 ? JAN MON 2025)" of+ Right expr -> isRecurring expr+ Left _ -> False+ rateRecurring =+ case parseCronText "rate(1 hour)" of+ Right expr -> isRecurring expr+ Left _ -> False+ oneTimeNonRecurring =+ case parseCronText "at(2025-11-16T09:30:00)" of+ Right expr -> not (isRecurring expr)+ Left _ -> False propRateMonotonic :: QC.Property propRateMonotonic =