From a01a18a6d13fc5a80dbac0c40f9886e0e2f0e353 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 20 Jun 2023 10:25:15 +0600 Subject: [PATCH] KrontabTemplateWrapper deprecation, extensions like "daily" --- CHANGELOG.md | 2 + .../kotlin/dev/inmo/krontab/KrontabConfig.kt | 165 ++++++++++++++++++ .../inmo/krontab/KrontabTemplateWrapper.kt | 8 + .../dev/inmo/krontab/SchedulerShortcuts.kt | 58 +++++- .../kotlin/dev/inmo/krontab/StringParser.kt | 148 +--------------- 5 files changed, 226 insertions(+), 155 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/KrontabConfig.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f957a1..19ffddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * New value class `KrontabConfig`. Since this update, it is preferable way to create `KronScheduler` instead of `KrontabTemplate` * You may configure krontab with builders using simple `KronScheduler` invoke extension +* New useful extensions like `KronScheduler.daily` +* `KrontabTemplateWrapper` is obsolete in favor to `KrontabConfig` ## 2.0.0 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KrontabConfig.kt b/src/commonMain/kotlin/dev/inmo/krontab/KrontabConfig.kt new file mode 100644 index 0000000..d0aa227 --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/KrontabConfig.kt @@ -0,0 +1,165 @@ +package dev.inmo.krontab + +import dev.inmo.krontab.internal.CronDateTimeScheduler +import dev.inmo.krontab.internal.CronDateTimeSchedulerTz +import dev.inmo.krontab.internal.createKronScheduler +import dev.inmo.krontab.internal.createKronSchedulerWithOffset +import dev.inmo.krontab.internal.millisecondsArrayDefault +import dev.inmo.krontab.internal.parseDaysOfMonth +import dev.inmo.krontab.internal.parseHours +import dev.inmo.krontab.internal.parseMilliseconds +import dev.inmo.krontab.internal.parseMinutes +import dev.inmo.krontab.internal.parseMonths +import dev.inmo.krontab.internal.parseOffset +import dev.inmo.krontab.internal.parseSeconds +import dev.inmo.krontab.internal.parseWeekDay +import dev.inmo.krontab.internal.parseYears +import dev.inmo.krontab.utils.Minutes +import korlibs.time.TimezoneOffset +import korlibs.time.minutes +import kotlinx.serialization.Serializable +import kotlin.jvm.JvmInline + +/** + * This value class contains [KrontabTemplate] + * + * * **seconds** + * * **minutes** + * * **hours** + * * **dayOfMonth** + * * **month** + * * **year** (optional) + * * **offset** (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00) + * * **dayOfWeek** (optional) (can be placed anywhere after month) + * * **milliseconds** (optional) (can be placed anywhere after month) (must be marked with `ms` at the end, for example: 500ms; 100-200ms) + * + * And each one (except of offsets) have next format: + * + * `{number}[,{number},...]` or `*` + * + * and {number} here is one of + * + * * {int}-{int} + * * {int}/{int} + * * */{int} + * * {int} + * * F + * * L + * + * Week days must be marked with `w` at the end, and starts with 0 which means Sunday. For example, 0w == Sunday. With + * weeks you can use syntax like with any number like seconds, for example: 0-2w means Sunday-Tuesday + * + * Additional info about ranges can be found in follow accordance: + * + * * Seconds ranges can be found in [secondsRange] + * * Minutes ranges can be found in [minutesRange] + * * Hours ranges can be found in [hoursRange] + * * Days of month ranges can be found in [dayOfMonthRange] + * * Months ranges can be found in [monthRange] + * * Years ranges can be found in [yearRange] (in fact - any [Int]) + * * WeekDay (timezone) ranges can be found in [dayOfWeekRange] + * * Milliseconds ranges can be found in [millisecondsRange] + * + * Examples: + * + * * "0/5 * * * *" for every five seconds triggering + * * "0/5,L * * * *" for every five seconds triggering and on 59 second + * * "0/15 30 * * *" for every 15th seconds in a half of each hour + * * "0/15 30 * * * 500ms" for every 15th seconds in a half of each hour when milliseconds equal to 500 + * * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of first, fifth and last days of may + * * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may in case if it will be in Sunday-Tuesday week days with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year + * * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 2021 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 2021 60o 0-2w 500ms" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 when milliseconds will be equal to 500 + * + * @see dev.inmo.krontab.internal.createKronScheduler + */ +@Serializable +@JvmInline +value class KrontabConfig( + @Suppress("MemberVisibilityCanBePrivate") + val template: KrontabTemplate +) { + /** + * Creates __new__ [KronScheduler] based on a [template] + * + * @return In case when offset parameter is absent in [template] will be used [createSimpleScheduler] method and + * returned [CronDateTimeScheduler]. In case when offset parameter there is in [template] [KrontabTemplate] will be used + * [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz] + */ + fun scheduler(): KronScheduler { + var offsetParsed: Int? = null + var dayOfWeekParsed: Array? = null + var yearParsed: Array? = null + var millisecondsParsed: Array? = null + val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = template.split(" ").also { + listOfNotNull( + it.getOrNull(5), + it.getOrNull(6), + it.getOrNull(7), + it.getOrNull(8) + ).forEach { + val offsetFromString = parseOffset(it) + val dayOfWeekFromString = parseWeekDay(it) + val millisecondsFromString = parseMilliseconds(it) + offsetParsed = offsetParsed ?: offsetFromString + dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString + millisecondsParsed = millisecondsParsed ?: millisecondsFromString + when { + dayOfWeekFromString != null || offsetFromString != null || millisecondsFromString != null -> return@forEach + yearParsed == null -> { + yearParsed = parseYears(it) + } + } + } + } + + val secondsParsed = parseSeconds(secondsSource) + val minutesParsed = parseMinutes(minutesSource) + val hoursParsed = parseHours(hoursSource) + val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource) + val monthParsed = parseMonths(monthSource) + + return offsetParsed ?.let { offset -> + createKronSchedulerWithOffset( + secondsParsed, + minutesParsed, + hoursParsed, + dayOfMonthParsed, + monthParsed, + yearParsed, + dayOfWeekParsed, + TimezoneOffset(offset.minutes), + millisecondsParsed ?: millisecondsArrayDefault + ) + } ?: createKronScheduler( + secondsParsed, + minutesParsed, + hoursParsed, + dayOfMonthParsed, + monthParsed, + yearParsed, + dayOfWeekParsed, + millisecondsParsed ?: millisecondsArrayDefault + ) + } + + /** + * Creates base [KronScheduler] using [scheduler] function. In case when returned [KronScheduler] is [KronSchedulerTz], + * it will be returned as is. Otherwise, will be created new [CronDateTimeSchedulerTz] with [defaultOffset] as + * offset + */ + fun scheduler(defaultOffset: Minutes): KronSchedulerTz { + val scheduler = scheduler() + return if (scheduler is KronSchedulerTz) { + scheduler + } else { + CronDateTimeSchedulerTz( + (scheduler as CronDateTimeScheduler).cronDateTime, + TimezoneOffset(defaultOffset.minutes) + ) + } + } +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt index 0cd06f6..d439c59 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt @@ -5,6 +5,10 @@ package dev.inmo.krontab * [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the * hood */ +@Deprecated( + "It is useless wrapper for KrontabTemplate. Use KrontabConfig instead", + ReplaceWith("KrontabConfig(template)", "dev.inmo.krontab.KrontabConfig") +) data class KrontabTemplateWrapper( val template: KrontabTemplate ) : KronScheduler by template.toKronScheduler() @@ -15,4 +19,8 @@ data class KrontabTemplateWrapper( * @see [toKronScheduler] * @see [KrontabTemplateWrapper] */ +@Deprecated( + "Will be removed in near major update with KrontabTemplateWrapper", + ReplaceWith("this.krontabConfig", "dev.inmo.krontab.krontabConfig") +) fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index 00dea44..ad88b58 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -10,57 +10,99 @@ internal val anyCronDateTime by lazy { internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!! /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] */ val AnyTimeScheduler: KronScheduler by lazy { CronDateTimeScheduler(anyCronDateTime) } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one millisecond + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one millisecond */ val EveryMillisecondScheduler: KronScheduler by lazy { buildSchedule { milliseconds { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one second */ val EverySecondScheduler: KronScheduler by lazy { buildSchedule { seconds { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one minute + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one minute */ val EveryMinuteScheduler: KronScheduler by lazy { buildSchedule { minutes { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one hour + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one hour */ val EveryHourScheduler: KronScheduler by lazy { buildSchedule { hours { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one day + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one day */ val EveryDayOfMonthScheduler: KronScheduler by lazy { buildSchedule { dayOfMonth { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one month + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one month */ val EveryMonthScheduler: KronScheduler by lazy { buildSchedule { months { 0 every 1 } } } /** - * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one year + * [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one year */ val EveryYearScheduler: KronScheduler by lazy { buildSchedule { years { 0 every 1 } } } + +/** + * Shortcut for [EveryMillisecondScheduler] + */ +inline val KronScheduler.Companion.everyMillisecond + get() = EveryMillisecondScheduler + +/** + * Shortcut for [EverySecondScheduler] + */ +inline val KronScheduler.Companion.everySecond + get() = EverySecondScheduler + +/** + * Shortcut for [EveryMinuteScheduler] + */ +inline val KronScheduler.Companion.everyMinute + get() = EveryMinuteScheduler + +/** + * Shortcut for [EveryHourScheduler] + */ +inline val KronScheduler.Companion.hourly + get() = EveryHourScheduler + +/** + * Shortcut for [EveryDayOfMonthScheduler] + */ +inline val KronScheduler.Companion.daily + get() = EveryDayOfMonthScheduler + +/** + * Shortcut for [EveryMonthScheduler] + */ +inline val KronScheduler.Companion.monthly + get() = EveryMonthScheduler + +/** + * Shortcut for [EveryYearScheduler] + */ +inline val KronScheduler.Companion.annually + get() = EveryYearScheduler diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index 79a71c3..292fbe5 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -1,11 +1,7 @@ package dev.inmo.krontab -import korlibs.time.TimezoneOffset -import korlibs.time.minutes import dev.inmo.krontab.internal.* import dev.inmo.krontab.utils.Minutes -import kotlinx.serialization.Serializable -import kotlin.jvm.JvmInline /** * @see createSimpleScheduler @@ -13,149 +9,7 @@ import kotlin.jvm.JvmInline */ typealias KrontabTemplate = String -/** - * This value class contains [KrontabTemplate] - * - * * **seconds** - * * **minutes** - * * **hours** - * * **dayOfMonth** - * * **month** - * * **year** (optional) - * * **offset** (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00) - * * **dayOfWeek** (optional) (can be placed anywhere after month) - * * **milliseconds** (optional) (can be placed anywhere after month) (must be marked with `ms` at the end, for example: 500ms; 100-200ms) - * - * And each one (except of offsets) have next format: - * - * `{number}[,{number},...]` or `*` - * - * and {number} here is one of - * - * * {int}-{int} - * * {int}/{int} - * * */{int} - * * {int} - * * F - * * L - * - * Week days must be marked with `w` at the end, and starts with 0 which means Sunday. For example, 0w == Sunday. With - * weeks you can use syntax like with any number like seconds, for example: 0-2w means Sunday-Tuesday - * - * Additional info about ranges can be found in follow accordance: - * - * * Seconds ranges can be found in [secondsRange] - * * Minutes ranges can be found in [minutesRange] - * * Hours ranges can be found in [hoursRange] - * * Days of month ranges can be found in [dayOfMonthRange] - * * Months ranges can be found in [monthRange] - * * Years ranges can be found in [yearRange] (in fact - any [Int]) - * * WeekDay (timezone) ranges can be found in [dayOfWeekRange] - * * Milliseconds ranges can be found in [millisecondsRange] - * - * Examples: - * - * * "0/5 * * * *" for every five seconds triggering - * * "0/5,L * * * *" for every five seconds triggering and on 59 second - * * "0/15 30 * * *" for every 15th seconds in a half of each hour - * * "0/15 30 * * * 500ms" for every 15th seconds in a half of each hour when milliseconds equal to 500 - * * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of first, fifth and last days of may - * * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may with timezone UTC+01:00 - * * "1 2 3 F,4,L 5 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may in case if it will be in Sunday-Tuesday week days with timezone UTC+01:00 - * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year - * * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year with timezone UTC+01:00 - * * "1 2 3 F,4,L 5 2021 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 - * * "1 2 3 F,4,L 5 2021 60o 0-2w 500ms" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 when milliseconds will be equal to 500 - * - * @see dev.inmo.krontab.internal.createKronScheduler - */ -@Serializable -@JvmInline -value class KrontabConfig( - @Suppress("MemberVisibilityCanBePrivate") - val template: KrontabTemplate -) { - /** - * Creates __new__ [KronScheduler] based on a [template] - * - * @return In case when offset parameter is absent in [template] will be used [createSimpleScheduler] method and - * returned [CronDateTimeScheduler]. In case when offset parameter there is in [template] [KrontabTemplate] will be used - * [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz] - */ - fun scheduler(): KronScheduler { - var offsetParsed: Int? = null - var dayOfWeekParsed: Array? = null - var yearParsed: Array? = null - var millisecondsParsed: Array? = null - val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = template.split(" ").also { - listOfNotNull( - it.getOrNull(5), - it.getOrNull(6), - it.getOrNull(7), - it.getOrNull(8) - ).forEach { - val offsetFromString = parseOffset(it) - val dayOfWeekFromString = parseWeekDay(it) - val millisecondsFromString = parseMilliseconds(it) - offsetParsed = offsetParsed ?: offsetFromString - dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString - millisecondsParsed = millisecondsParsed ?: millisecondsFromString - when { - dayOfWeekFromString != null || offsetFromString != null || millisecondsFromString != null -> return@forEach - yearParsed == null -> { - yearParsed = parseYears(it) - } - } - } - } - - val secondsParsed = parseSeconds(secondsSource) - val minutesParsed = parseMinutes(minutesSource) - val hoursParsed = parseHours(hoursSource) - val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource) - val monthParsed = parseMonths(monthSource) - - return offsetParsed ?.let { offset -> - createKronSchedulerWithOffset( - secondsParsed, - minutesParsed, - hoursParsed, - dayOfMonthParsed, - monthParsed, - yearParsed, - dayOfWeekParsed, - TimezoneOffset(offset.minutes), - millisecondsParsed ?: millisecondsArrayDefault - ) - } ?: createKronScheduler( - secondsParsed, - minutesParsed, - hoursParsed, - dayOfMonthParsed, - monthParsed, - yearParsed, - dayOfWeekParsed, - millisecondsParsed ?: millisecondsArrayDefault - ) - } - - /** - * Creates base [KronScheduler] using [scheduler] function. In case when returned [KronScheduler] is [KronSchedulerTz], - * it will be returned as is. Otherwise, will be created new [CronDateTimeSchedulerTz] with [defaultOffset] as - * offset - */ - fun scheduler(defaultOffset: Minutes): KronSchedulerTz { - val scheduler = scheduler() - return if (scheduler is KronSchedulerTz) { - scheduler - } else { - CronDateTimeSchedulerTz( - (scheduler as CronDateTimeScheduler).cronDateTime, - TimezoneOffset(defaultOffset.minutes) - ) - } - } -} +inline fun KrontabTemplate.krontabConfig() = KrontabConfig(this) /** * Parse [incoming] string and adapt according to next format: "* * * * *" where order of things: