From 0a2b5ef6a0bb515bff94b11dc09d4cad02769205 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 19:42:10 +0600 Subject: [PATCH 1/9] start 0.5.0 --- CHANGELOG.md | 2 ++ build.gradle | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8f959..871dd0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.5.0 + ## 0.4.2 * Versions diff --git a/build.gradle b/build.gradle index 6873baf..fe7849f 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ plugins { id "org.jetbrains.dokka" version "$dokka_version" } -project.version = "0.4.2" +project.version = "0.5.0" project.group = "dev.inmo" apply from: "publish.gradle" From 476239faa691eaee4268a07e5e68801547fab384 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 20:13:47 +0600 Subject: [PATCH 2/9] CronDateTimeScheduler now is internal (again :( ) --- CHANGELOG.md | 8 ++++ .../dev/inmo/krontab/KronSchedulersMerging.kt | 41 +++++++++++++++++++ .../collection/CollectionKronScheduler.kt | 5 +-- .../krontab/internal/CronDateTimeScheduler.kt | 24 ++++++----- 4 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 871dd0a..49d604a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## 0.5.0 +**BREAKING CHANGES** + +* `CronDateTimeScheduler` has been marked as `internal` and no longer accessible outside of internal functions + * Old methods `merge` and `plus` related to `CronDateTimeScheduler` has been marked as `deprecated` and changed their + parameters types - it is `KronScheduler` now +* New methods `merge` has been added + + ## 0.4.2 * Versions diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt new file mode 100644 index 0000000..f5a682f --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt @@ -0,0 +1,41 @@ +package dev.inmo.krontab + +import dev.inmo.krontab.collection.CollectionKronScheduler +import dev.inmo.krontab.internal.CronDateTime +import dev.inmo.krontab.internal.CronDateTimeScheduler + +/** + * Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator] + * + * @see CollectionKronScheduler + * @see CollectionKronScheduler.include + */ +fun Iterator.merge(): KronScheduler { + val cronDateTimes = mutableListOf() + val collectionScheduler = CollectionKronScheduler() + forEach { + when (it) { + is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes) + else -> collectionScheduler.include(it) + } + } + if (cronDateTimes.isNotEmpty()) { + collectionScheduler.include(CronDateTimeScheduler(cronDateTimes)) + } + return collectionScheduler +} + +/** + * Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator] + * + * @see CollectionKronScheduler + * @see CollectionKronScheduler.include + */ +@Suppress("NOTHING_TO_INLINE") +inline fun Iterable.merge(): KronScheduler = iterator().merge() + +/** + * @return Vararg shortcut for [merge] + */ +@Suppress("NOTHING_TO_INLINE") +inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.iterator().merge() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index f5a4263..978140c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -5,7 +5,6 @@ import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.anyCronDateTime import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTimeScheduler -import dev.inmo.krontab.internal.merge import dev.inmo.krontab.internal.toNearDateTime /** @@ -20,7 +19,7 @@ data class CollectionKronScheduler internal constructor( * Add [kronScheduler] into its [schedulers] list * * * When [kronScheduler] is [CronDateTimeScheduler] it will merge all [CronDateTimeScheduler]s from [schedulers] list - * and this [kronScheduler] using [merge] function + * and this [kronScheduler] using [mergeCronDateTimeSchedulers] function * * When [kronScheduler] is [CollectionKronScheduler] it this instance will include all [kronScheduler] * [schedulers] * * Otherwise [kronScheduler] will be added to [schedulers] list @@ -37,7 +36,7 @@ data class CollectionKronScheduler internal constructor( } } schedulers.add( - merge(resultCronDateTimes) + mergeCronDateTimeSchedulers(resultCronDateTimes) ) } is CollectionKronScheduler -> kronScheduler.schedulers.forEach { diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index 6f8fcd8..485fb3c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -3,6 +3,7 @@ package dev.inmo.krontab.internal import com.soywiz.klock.DateTime import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.anyCronDateTime +import dev.inmo.krontab.collection.plus /** * Cron-oriented realisation of [KronScheduler] @@ -17,8 +18,7 @@ import dev.inmo.krontab.anyCronDateTime * @see dev.inmo.krontab.builder.buildSchedule * @see dev.inmo.krontab.builder.SchedulerBuilder */ -@Deprecated("This class will get internal status in future") -data class CronDateTimeScheduler internal constructor( +internal data class CronDateTimeScheduler internal constructor( internal val cronDateTimes: List ) : KronScheduler { /** @@ -31,22 +31,26 @@ data class CronDateTimeScheduler internal constructor( } } +internal fun mergeCronDateTimeSchedulers(schedulers: List) = CronDateTimeScheduler( + schedulers.flatMap { it.cronDateTimes } +) + /** * @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of * [kronDateTimeSchedulers] included */ -@Suppress("NOTHING_TO_INLINE") -fun merge(kronDateTimeSchedulers: List) = CronDateTimeScheduler( - kronDateTimeSchedulers.flatMap { it.cronDateTimes }.distinct() -) +@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab")) +fun merge(kronSchedulers: List) = kronSchedulers.apply { dev.inmo.krontab.merge() } /** - * @return Vararg shortcyut for [merge] + * @return Vararg shortcut for [dev.inmo.krontab.merge] */ @Suppress("NOTHING_TO_INLINE") -inline fun merge(vararg kronDateTimeSchedulers: CronDateTimeScheduler) = merge(kronDateTimeSchedulers.toList()) +@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab")) +inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.apply { dev.inmo.krontab.merge() } /** - * Use [merge] operation to internalcreate new [CronDateTimeScheduler] with all [CronDateTimeScheduler.cronDateTimes] + * @return Vararg shortcut for [dev.inmo.krontab.merge] */ @Suppress("NOTHING_TO_INLINE") -inline fun CronDateTimeScheduler.plus(other: CronDateTimeScheduler) = merge(this, other) +@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab")) +inline fun KronScheduler.plus(other: KronScheduler) = this + other From 365adb10166f0c98c6fb7fe679b22fad290e67f8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 21:35:08 +0600 Subject: [PATCH 3/9] add years, KrontabWrapper, new function to create scheduler from template and update readme --- README.md | 22 ++++++------ .../kotlin/dev/inmo/krontab/Executes.kt | 6 +++- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 5 ++- .../inmo/krontab/KrontabTemplateWrapper.kt | 5 +++ .../dev/inmo/krontab/SchedulerShortcuts.kt | 10 ++++++ .../kotlin/dev/inmo/krontab/StringParser.kt | 18 ++++++++-- .../inmo/krontab/builder/SchedulerBuilder.kt | 34 +++++++++++++------ .../dev/inmo/krontab/builder/TimeBuilder.kt | 18 +++++----- .../collection/CollectionKronScheduler.kt | 5 ++- .../dev/inmo/krontab/internal/CronDateTime.kt | 19 +++++++++-- .../krontab/internal/CronDateTimeScheduler.kt | 10 +++--- .../dev/inmo/krontab/internal/Parser.kt | 23 ++++++++----- .../dev/inmo/krontab/internal/Ranges.kt | 1 + .../dev/inmo/krontab/utils/SchedulerFlow.kt | 2 +- 14 files changed, 122 insertions(+), 56 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt diff --git a/README.md b/README.md index 9e37851..376cb2e 100644 --- a/README.md +++ b/README.md @@ -53,18 +53,18 @@ For old version of Gradle, instead of `implementation` word developers must use Developers can use more simple way to configure repeat times is string. String configuring like a `crontab`, but with a little bit different meanings: ``` -/-------- Seconds -| /------ Minutes -| | /---- Hours -| | | /-- Days of months -| | | | / Months -| | | | | -* * * * * +/---------- Seconds +| /-------- Minutes +| | /------ Hours +| | | /---- Days of months +| | | | /-- Months +| | | | | / (optional) Year +* * * * * * ``` It is different with original `crontab` syntax for the reason, that expected that in practice developers -will use seconds and minutes with more probability than months (for example). In fact, developers will use something -like: +will use seconds and minutes with more probability than months (for example) or even years. In fact, developers will use +something like: ```kotlin doWhile("/5 * * * *") { @@ -73,7 +73,7 @@ doWhile("/5 * * * *") { } ``` -Or more version: +An other version: ```kotlin doInfinity("/5 * * * *") { @@ -85,7 +85,7 @@ Both of examples will print `Called` message every five seconds. ### Config via builder -Also this library currently supports DSL for creating the same goals: +Also, this library currently supports DSL for creating the same goals: ```kotlin val kronScheduler = buildSchedule { diff --git a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt index 18daaff..f3077a9 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/Executes.kt @@ -7,9 +7,13 @@ import kotlinx.coroutines.delay * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation. * * WARNING!!! If you want to launch it in parallel, you must do this explicitly. + * + * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately */ suspend inline fun KronScheduler.doOnce(noinline block: suspend () -> T): T { - delay((next() - DateTime.now()).millisecondsLong) + next() ?.let { + delay((it - DateTime.now()).millisecondsLong) + } return block() } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index 4c5385c..b511b3f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime +import dev.inmo.krontab.internal.toNearDateTime /** * This interface was created for abstraction of [next] operation. Currently, there is only @@ -17,5 +18,7 @@ interface KronScheduler { * * @see dev.inmo.krontab.internal.CronDateTimeScheduler.next */ - suspend fun next(relatively: DateTime = DateTime.now()): DateTime + suspend fun next(relatively: DateTime = DateTime.now()): DateTime? } + +suspend fun KronScheduler.forceNext(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt new file mode 100644 index 0000000..480211f --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt @@ -0,0 +1,5 @@ +package dev.inmo.krontab + +data class KrontabTemplateWrapper( + val template: KrontabTemplate +) : KronScheduler by template.toKronScheduler() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index c3cadc7..ed641e6 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -1,12 +1,15 @@ package dev.inmo.krontab +import com.soywiz.klock.DateTime import dev.inmo.krontab.builder.buildSchedule +import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTime import dev.inmo.krontab.internal.CronDateTimeScheduler internal val anyCronDateTime by lazy { CronDateTime() } +internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!! /** * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] @@ -48,4 +51,11 @@ val EveryDayOfMonthScheduler: KronScheduler by lazy { */ val EveryMonthScheduler: KronScheduler by lazy { buildSchedule { months { 0 every 1 } } +} + +/** + * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one year + */ +val EveryYearScheduler: KronScheduler by lazy { + buildSchedule { years { 0 every 1 } } } \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index 845ad5d..d41c5b4 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -16,6 +16,7 @@ typealias KrontabTemplate = String * * hours * * dayOfMonth * * month + * * (optional) year * * And each one have next format: * @@ -37,6 +38,7 @@ typealias KrontabTemplate = String * * 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]) * * Examples: * @@ -44,20 +46,25 @@ typealias KrontabTemplate = String * * "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 * * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may + * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year * * @see dev.inmo.krontab.internal.createKronScheduler */ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler { - val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ") + val yearSource: String? + val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also { + yearSource = it.getOrNull(5) + } val secondsParsed = parseSeconds(secondsSource) val minutesParsed = parseMinutes(minutesSource) val hoursParsed = parseHours(hoursSource) val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource) val monthParsed = parseMonths(monthSource) + val yearParsed = parseYears(yearSource) return createKronScheduler( - secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed ) } @@ -69,4 +76,9 @@ fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleSchedu /** * Shortcut for [buildSchedule] */ -fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this) \ No newline at end of file +fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this) + +/** + * Shortcut for [buildSchedule] + */ +fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this) \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt index d1db013..acc1334 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt @@ -21,20 +21,21 @@ class SchedulerBuilder( private var minutes: Array? = null, private var hours: Array? = null, private var dayOfMonth: Array? = null, - private var month: Array? = null + private var month: Array? = null, + private var year: Array? = null ) { - private fun callAndReturn( - initial: Array?, + private fun > callAndReturn( + initial: Array?, builder: T, block: T.() -> Unit - ): Array? { + ): List? { builder.block() val builderValue = builder.build() return initial ?.let { builderValue ?.let { _ -> - (it + builderValue).distinct().toTypedArray() + (it + builderValue).distinct() } ?: builderValue } ?: builderValue } @@ -47,7 +48,7 @@ class SchedulerBuilder( seconds, SecondsBuilder(), block - ) + ) ?.toTypedArray() } /** @@ -58,7 +59,7 @@ class SchedulerBuilder( minutes, MinutesBuilder(), block - ) + ) ?.toTypedArray() } /** @@ -69,7 +70,7 @@ class SchedulerBuilder( hours, HoursBuilder(), block - ) + ) ?.toTypedArray() } /** @@ -80,7 +81,7 @@ class SchedulerBuilder( dayOfMonth, DaysOfMonthBuilder(), block - ) + ) ?.toTypedArray() } /** @@ -91,7 +92,18 @@ class SchedulerBuilder( month, MonthsBuilder(), block - ) + ) ?.toTypedArray() + } + + /** + * Starts an year block + */ + fun years(block: YearsBuilder.() -> Unit) { + year = callAndReturn( + year, + YearsBuilder(), + block + ) ?.toTypedArray() } /** @@ -100,5 +112,5 @@ class SchedulerBuilder( * @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.internal.createKronScheduler */ - fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month) + fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt index a250cbe..c4cc8a4 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt @@ -7,8 +7,9 @@ import dev.inmo.krontab.utils.clamp * This class was created for incapsulation of builder work with specified [restrictionsRange]. For example, * [include] function of [TimeBuilder] will always [clamp] incoming data using its [restrictionsRange] */ -sealed class TimeBuilder ( - private val restrictionsRange: IntRange +sealed class TimeBuilder ( + private val restrictionsRange: IntRange, + private val converter: Converter ) { private var result: Set? = null @@ -119,11 +120,12 @@ sealed class TimeBuilder ( */ fun includeFirst() = at(restrictionsRange.first) - internal fun build() = result ?.map { it.toByte() } ?.toTypedArray() + internal fun build() = result ?.map(converter) } -class SecondsBuilder : TimeBuilder(secondsRange) -class MinutesBuilder : TimeBuilder(minutesRange) -class HoursBuilder : TimeBuilder(hoursRange) -class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange) -class MonthsBuilder : TimeBuilder(monthRange) +class SecondsBuilder : TimeBuilder(secondsRange, intToByteConverter) +class MinutesBuilder : TimeBuilder(minutesRange, intToByteConverter) +class HoursBuilder : TimeBuilder(hoursRange, intToByteConverter) +class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange, intToByteConverter) +class MonthsBuilder : TimeBuilder(monthRange, intToByteConverter) +class YearsBuilder : TimeBuilder(yearRange, intToIntConverter) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index 978140c..1f552b2 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -1,8 +1,7 @@ package dev.inmo.krontab.collection import com.soywiz.klock.DateTime -import dev.inmo.krontab.KronScheduler -import dev.inmo.krontab.anyCronDateTime +import dev.inmo.krontab.* import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTimeScheduler import dev.inmo.krontab.internal.toNearDateTime @@ -47,6 +46,6 @@ data class CollectionKronScheduler internal constructor( } override suspend fun next(relatively: DateTime): DateTime { - return schedulers.minOfOrNull { it.next(relatively) } ?: anyCronDateTime.toNearDateTime(relatively) + return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively) } } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index ed89c96..269cd9b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -12,6 +12,7 @@ import dev.inmo.krontab.KronScheduler * @param seconds 0-59 */ internal data class CronDateTime( + val year: Int? = null, val month: Byte? = null, val dayOfMonth: Byte? = null, val hours: Byte? = null, @@ -19,6 +20,7 @@ internal data class CronDateTime( val seconds: Byte? = null ) { init { + check(year ?.let { it in yearRange } ?: true) check(month ?.let { it in monthRange } ?: true) check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true) check(hours?.let { it in hoursRange } ?: true) @@ -32,7 +34,7 @@ internal data class CronDateTime( /** * @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo] */ -internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime { +internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { var current = relativelyTo seconds?.let { @@ -63,7 +65,13 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now() month ?.let { val left = it - current.month0 - current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left) + current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left) + } + + year ?.let { + if (current.yearInt != it) { + return null + } } return current @@ -77,7 +85,8 @@ internal fun createKronScheduler( minutes: Array? = null, hours: Array? = null, dayOfMonth: Array? = null, - month: Array? = null + month: Array? = null, + years: Array? = null ): KronScheduler { val resultCronDateTimes = mutableListOf(CronDateTime()) @@ -101,5 +110,9 @@ internal fun createKronScheduler( previousCronDateTime.copy(month = currentTime) } + years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> + previousCronDateTime.copy(year = currentTime) + } + return CronDateTimeScheduler(resultCronDateTimes.toList()) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index 485fb3c..23de605 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -1,8 +1,7 @@ package dev.inmo.krontab.internal import com.soywiz.klock.DateTime -import dev.inmo.krontab.KronScheduler -import dev.inmo.krontab.anyCronDateTime +import dev.inmo.krontab.* import dev.inmo.krontab.collection.plus /** @@ -14,6 +13,7 @@ import dev.inmo.krontab.collection.plus * @see dev.inmo.krontab.EveryHourScheduler * @see dev.inmo.krontab.EveryDayOfMonthScheduler * @see dev.inmo.krontab.EveryMonthScheduler + * @see dev.inmo.krontab.EveryYearScheduler * * @see dev.inmo.krontab.builder.buildSchedule * @see dev.inmo.krontab.builder.SchedulerBuilder @@ -22,12 +22,12 @@ internal data class CronDateTimeScheduler internal constructor( internal val cronDateTimes: List ) : KronScheduler { /** - * @return Near date using [cronDateTimes] list and getting the [Iterable.min] one + * @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one * * @see toNearDateTime */ override suspend fun next(relatively: DateTime): DateTime { - return cronDateTimes.map { it.toNearDateTime(relatively) }.minOrNull() ?: anyCronDateTime.toNearDateTime(relatively) + return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) } } @@ -37,7 +37,7 @@ internal fun mergeCronDateTimeSchedulers(schedulers: List /** * @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of - * [kronDateTimeSchedulers] included + * [kronSchedulers] included */ @Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab")) fun merge(kronSchedulers: List) = kronSchedulers.apply { dev.inmo.krontab.merge() } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt index 04b71ff..f2f8648 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt @@ -2,7 +2,11 @@ package dev.inmo.krontab.internal import dev.inmo.krontab.utils.clamp -private fun createSimpleScheduler(from: String, dataRange: IntRange): Array? { +typealias Converter = (Int) -> T + +internal val intToByteConverter: Converter = { it: Int -> it.toByte() } +internal val intToIntConverter: Converter = { it: Int -> it } +private fun createSimpleScheduler(from: String, dataRange: IntRange, dataConverter: Converter): List? { val things = from.split(",") val results = things.flatMap { @@ -31,18 +35,19 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange): Array.fillWith( +internal fun Array.fillWith( whereToPut: MutableList, - createFactory: (CronDateTime, Byte) -> CronDateTime + createFactory: (CronDateTime, T) -> CronDateTime ) { val previousValues = whereToPut.toList() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt index 58586f1..ec4359b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt @@ -1,5 +1,6 @@ package dev.inmo.krontab.internal +internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE internal val monthRange = 0 .. 11 internal val dayOfMonthRange = 0 .. 30 internal val hoursRange = 0 .. 23 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index eca31f2..14cc761 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -17,7 +17,7 @@ class SchedulerFlow( override suspend fun collectSafely(collector: FlowCollector) { while (true) { val now = DateTime.now() - val nextTime = scheduler.next(now) + val nextTime = scheduler.next(now) ?: break val sleepDelay = (nextTime - now).millisecondsLong delay(sleepDelay) collector.emit(nextTime) From bbb4f4172398d45ca91a8a48c0dfc13598bb01d8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 21:55:06 +0600 Subject: [PATCH 4/9] fill changelog --- CHANGELOG.md | 11 ++++++++++- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 5 ++++- .../dev/inmo/krontab/KrontabTemplateWrapper.kt | 13 +++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d604a..f446586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.0 +## 0.5.0 Years **BREAKING CHANGES** @@ -8,6 +8,15 @@ * Old methods `merge` and `plus` related to `CronDateTimeScheduler` has been marked as `deprecated` and changed their parameters types - it is `KronScheduler` now * New methods `merge` has been added +* **`KronScheduler#next` method now is nullable. Use `nextOrRelative`/`nextOrNow` to get next time certainly** +* **Years was added as optional part of krontab template and opportunity in `SchedulerBuilder`** + * New builder `YearsBuilder` + * `SchedulerFlow#collectSafely` will be normally (without exceptions) finish when `next` of scheduler will return + null +* `KronScheduler#doOnce` will run code immediately in case when `next` is returning null value +* `KrontabTemplateWrapper` has been added +* New extension `KrontabTemplate#toKronScheduler` (works as `toSchedule`) +* **Fixed issue related to the fact that `toNearDateTime` of `CronDateTime` incorrectly handled months ## 0.4.2 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index b511b3f..b7e214a 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -21,4 +21,7 @@ interface KronScheduler { suspend fun next(relatively: DateTime = DateTime.now()): DateTime? } -suspend fun KronScheduler.forceNext(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) +suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) +suspend fun KronScheduler.nextOrNow(): DateTime = DateTime.now().let { + next(it) ?: getAnyNext(it) +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt index 480211f..0cd06f6 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KrontabTemplateWrapper.kt @@ -1,5 +1,18 @@ package dev.inmo.krontab +/** + * This class contains [template] and can be simply serialized/deserialized. In fact that class will work as + * [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the + * hood + */ data class KrontabTemplateWrapper( val template: KrontabTemplate ) : KronScheduler by template.toKronScheduler() + +/** + * Will create [KrontabTemplateWrapper] from [this] [KrontabTemplate] + * + * @see [toKronScheduler] + * @see [KrontabTemplateWrapper] + */ +fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this) From 0376eb791037977394e05f94a1cfce5d03042e35 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 22:43:32 +0600 Subject: [PATCH 5/9] add android target --- build.gradle | 57 ++++++++++++++++++- gradle.properties | 21 +++++++ src/main/AndroidManifest.xml | 1 + .../inmo/krontab/KronSchedulerWorkRequest.kt | 18 ++++++ 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/main/AndroidManifest.xml create mode 100644 src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt diff --git a/build.gradle b/build.gradle index fe7849f..d9db98d 100644 --- a/build.gradle +++ b/build.gradle @@ -3,11 +3,14 @@ buildscript { mavenLocal() jcenter() mavenCentral() + google() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.github.breadmoirai:github-release:$github_release_plugin_version" + classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" + classpath 'com.android.tools.build:gradle:4.0.2' } } @@ -15,8 +18,9 @@ plugins { id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" id "org.jetbrains.dokka" version "$dokka_version" } +apply plugin: "com.android.library" -project.version = "0.5.0" +project.version = "$version" project.group = "dev.inmo" apply from: "publish.gradle" @@ -27,6 +31,7 @@ repositories { jcenter() mavenCentral() maven { url "https://kotlin.bintray.com/kotlinx" } + google() } apply from: './dokka.gradle' @@ -37,6 +42,9 @@ kotlin { browser() nodejs() } + android { + publishAllLibraryVariants() + } sourceSets { @@ -48,6 +56,13 @@ kotlin { api "com.soywiz.korlibs.klock:klock:$klockVersion" } } + + androidMain { + dependencies { + api "androidx.work:work-runtime-ktx:$androidx_work_version" + } + } + commonTest { dependencies { implementation kotlin('test-common') @@ -64,5 +79,45 @@ kotlin { implementation kotlin('test-js') } } + androidTest { + dependencies { + implementation kotlin('test-junit') + } + } + } +} + +apply plugin: 'com.getkeepsafe.dexcount' + +android { + compileSdkVersion "$android_compileSdkVersion".toInteger() + buildToolsVersion "$android_buildToolsVersion" + + defaultConfig { + minSdkVersion "$android_minSdkVersion".toInteger() + targetSdkVersion "$android_compileSdkVersion".toInteger() + versionCode "${android_code_version}".toInteger() + versionName "$version" + } + buildTypes { + release { + minifyEnabled false + } + debug { + debuggable true + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } } diff --git a/gradle.properties b/gradle.properties index 6f206fb..78ca93d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,8 @@ kotlin.js.generate.externals=true kotlin.incremental=true kotlin.incremental.js=true kotlin.incremental.multiplatform=true +android.useAndroidX=true +android.enableJetifier=true kotlin_version=1.4.21 @@ -13,5 +15,24 @@ dokka_version=1.4.20 klockVersion=2.0.3 +## Github reease + github_release_plugin_version=2.2.12 +## Android + +android_minSdkVersion=19 +android_compileSdkVersion=30 +android_buildToolsVersion=30.0.2 +dexcount_version=2.0.0 +junit_version=4.12 +test_ext_junit_version=1.1.2 +espresso_core=3.3.0 + +androidx_work_version=2.4.0 + +## Common + +version=0.5.0 +android_code_version=1 + diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0ecc4fb --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt new file mode 100644 index 0000000..534f820 --- /dev/null +++ b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt @@ -0,0 +1,18 @@ +package dev.inmo.krontab + +import android.content.Context +import androidx.work.* + +//fun KronScheduler.workRequest(context: Context) = OneTimeWorkRequestBuilder<>() +// +//abstract class KronSchedulerWorkRequest( +// context: Context, +// workerParams: WorkerParameters +//) : CoroutineWorker( +// context, +// workerParams +//) { +// override suspend fun doWork(): Result { +// +// } +//} From 3cce44664f2cc3da08ebc43ae7ea9c67d7d3f73d Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 22:56:35 +0600 Subject: [PATCH 6/9] fix tests issue --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index d9db98d..44f9d63 100644 --- a/build.gradle +++ b/build.gradle @@ -119,5 +119,8 @@ android { sourceSets { main.java.srcDirs += 'src/main/kotlin' + test { + java.srcDir file("src/jvmTest") + } } } From 3f132da62d520e46e6a9fa0b049b95fdebc37f9e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 2 Jan 2021 23:56:31 +0600 Subject: [PATCH 7/9] add KronSchedulerWork --- CHANGELOG.md | 2 +- .../dev/inmo/krontab/KronSchedulerWork.kt | 94 +++++++++++++++++++ .../inmo/krontab/KronSchedulerWorkRequest.kt | 18 ---- 3 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt delete mode 100644 src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index f446586..9bda26c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ * `KrontabTemplateWrapper` has been added * New extension `KrontabTemplate#toKronScheduler` (works as `toSchedule`) * **Fixed issue related to the fact that `toNearDateTime` of `CronDateTime` incorrectly handled months - +* **Android target has been added** ## 0.4.2 diff --git a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt new file mode 100644 index 0000000..c770cc3 --- /dev/null +++ b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt @@ -0,0 +1,94 @@ +package dev.inmo.krontab + +import android.content.Context +import androidx.work.* +import com.soywiz.klock.DateTime +import java.util.concurrent.TimeUnit + +suspend fun Context.enqueueKronSchedulerWork( + workName: String, + delayMillis: Long, + workClass: Class, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit +) = WorkManager.getInstance(applicationContext).enqueueUniqueWork( + workName, + ExistingWorkPolicy.REPLACE, + OneTimeWorkRequest.Builder(workClass).apply { + setInitialDelay(delayMillis, TimeUnit.MILLISECONDS) + setUpRequest() + }.build() +) +suspend inline fun Context.enqueueKronSchedulerWork( + workName: String, + delayMillis: Long, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit +) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, setUpRequest) + +suspend fun Context.enqueueKronSchedulerWork( + workName: String, + initialScheduler: KronScheduler, + workClass: Class, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit +): Operation? { + val now = DateTime.now() + val nextTriggerTime = initialScheduler.next(now) + val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return null + + return enqueueKronSchedulerWork(workName, delayMillis, workClass, setUpRequest) +} +suspend inline fun Context.enqueueKronSchedulerWork( + workName: String, + initialScheduler: KronScheduler, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit +) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, setUpRequest) + +/** + * Use this class as an super class in case you wish to implement krontab-based enqueuing of works + */ +abstract class KronSchedulerWork( + context: Context, + workerParams: WorkerParameters +) : CoroutineWorker( + context, + workerParams +) { + protected abstract val workName: String + protected open val prolongWork: Boolean = true + protected open val prolongOnFailure + get() = prolongWork + protected open val prolongOnException = false + protected open val prolongOnNextAbsent = false + + protected abstract suspend fun kronScheduler(): KronScheduler? + protected abstract suspend fun onWork(): Result + protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest() {} + + protected suspend fun prolong() { + val now = DateTime.now() + val nextTriggerTime = kronScheduler() ?.let { + if (prolongOnNextAbsent) { + it.nextOrRelative(now) + } else { + it.next(now) + } + } + val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return + + applicationContext.enqueueKronSchedulerWork(workName, delayMillis, this::class.java) { setUpRequest() } + } + + override suspend fun doWork(): Result { + val result = try { + onWork() + } catch (e: Throwable) { + if (prolongOnException) { + prolong() + } + throw e + } + if (result !is Result.Failure || prolongOnFailure) { + prolong() + } + return result + } +} diff --git a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt deleted file mode 100644 index 534f820..0000000 --- a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWorkRequest.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.inmo.krontab - -import android.content.Context -import androidx.work.* - -//fun KronScheduler.workRequest(context: Context) = OneTimeWorkRequestBuilder<>() -// -//abstract class KronSchedulerWorkRequest( -// context: Context, -// workerParams: WorkerParameters -//) : CoroutineWorker( -// context, -// workerParams -//) { -// override suspend fun doWork(): Result { -// -// } -//} From e04d14ccb1ef36d15c13b60ebb3c848ec918ab86 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 3 Jan 2021 12:45:38 +0600 Subject: [PATCH 8/9] fixed issues --- .../dev/inmo/krontab/KronSchedulerWork.kt | 105 +++++++++++++----- .../krontab/KrontabTemplateSchedulerWork.kt | 68 ++++++++++++ 2 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt diff --git a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt index c770cc3..e22d4b2 100644 --- a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt +++ b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt @@ -5,45 +5,68 @@ import androidx.work.* import com.soywiz.klock.DateTime import java.util.concurrent.TimeUnit +/** + * This method will enqueue [OneTimeWorkRequest] with [workName] and [existingWorkPolicy]. Use [setUpRequest] callback + * in case you need some additional actions to do before request will be enqueued + */ suspend fun Context.enqueueKronSchedulerWork( workName: String, delayMillis: Long, workClass: Class, - setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} ) = WorkManager.getInstance(applicationContext).enqueueUniqueWork( workName, - ExistingWorkPolicy.REPLACE, + existingWorkPolicy, OneTimeWorkRequest.Builder(workClass).apply { setInitialDelay(delayMillis, TimeUnit.MILLISECONDS) setUpRequest() }.build() ) +/** + * This method is shortcut for [enqueueKronSchedulerWork] with reified [T] parameter + */ suspend inline fun Context.enqueueKronSchedulerWork( workName: String, delayMillis: Long, - noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit -) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, setUpRequest) + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} +) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, existingWorkPolicy, setUpRequest) +/** + * This method is shortcut for [enqueueKronSchedulerWork] with [initialScheduler]. It will try to calculate delay by + * itself. In case if [KronScheduler.next] of [initialScheduler] will return null, work WILL NOT be enqueued + * + * @return null in case if [KronScheduler.next] of [initialScheduler] has returned null and work has not been enqueued + */ suspend fun Context.enqueueKronSchedulerWork( workName: String, initialScheduler: KronScheduler, workClass: Class, - setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} ): Operation? { val now = DateTime.now() val nextTriggerTime = initialScheduler.next(now) val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return null - return enqueueKronSchedulerWork(workName, delayMillis, workClass, setUpRequest) + return enqueueKronSchedulerWork(workName, delayMillis, workClass, existingWorkPolicy, setUpRequest) } +/** + * This method is shortcut for [enqueueKronSchedulerWork] with reified [T] + */ suspend inline fun Context.enqueueKronSchedulerWork( workName: String, initialScheduler: KronScheduler, - noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit -) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, setUpRequest) + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} +) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, existingWorkPolicy, setUpRequest) /** - * Use this class as an super class in case you wish to implement krontab-based enqueuing of works + * Use this class as a super class in case you wish to implement krontab-based enqueuing of works + * + * @see enqueueKronSchedulerWork + * @see KrontabTemplateSchedulerWork */ abstract class KronSchedulerWork( context: Context, @@ -52,29 +75,58 @@ abstract class KronSchedulerWork( context, workerParams ) { + /** + * This variable will be used to reorder new work after that one is happen + * + * @see prolongOnException + * @see prolongOnFailure + * @see prolongOnSuccess + */ protected abstract val workName: String - protected open val prolongWork: Boolean = true - protected open val prolongOnFailure - get() = prolongWork - protected open val prolongOnException = false - protected open val prolongOnNextAbsent = false + /** + * Set this to false in case when this work must not be enqueued after successful complete + */ + protected open val prolongOnSuccess: Boolean = true + /** + * Set this to false in case when this work must not be enqueued after failure complete + */ + protected open val prolongOnFailure + get() = prolongOnSuccess + /** + * Set this to false in case when this work must not be enqueued after exception happen + */ + protected open val prolongOnException = false + + /** + * [KronScheduler] of this method will be used to [prolong] this worker + */ protected abstract suspend fun kronScheduler(): KronScheduler? + + /** + * This method is replacement of [doWork]. It is required to wrap work with [prolong]ing and handling of complete + * state + */ protected abstract suspend fun onWork(): Result + + /** + * Override this method in case you have some additional settings for future [OneTimeWorkRequest] + */ protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest() {} + /** + * This method will [enqueueKronSchedulerWork] using [workName], [kronScheduler] and default + * [ExistingWorkPolicy.REPLACE]. You can call this method in case you want to enqueue work by yourself, but you must + * be sure that you set up to false [prolongOnSuccess], [prolongOnFailure] and [prolongOnException] + */ protected suspend fun prolong() { - val now = DateTime.now() - val nextTriggerTime = kronScheduler() ?.let { - if (prolongOnNextAbsent) { - it.nextOrRelative(now) - } else { - it.next(now) - } + applicationContext.enqueueKronSchedulerWork( + workName, + kronScheduler() ?: return, + this::class.java + ) { + setUpRequest() } - val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return - - applicationContext.enqueueKronSchedulerWork(workName, delayMillis, this::class.java) { setUpRequest() } } override suspend fun doWork(): Result { @@ -86,8 +138,9 @@ abstract class KronSchedulerWork( } throw e } - if (result !is Result.Failure || prolongOnFailure) { - prolong() + when (result) { + is Result.Failure -> if (prolongOnFailure) prolong() + is Result.Success -> if (prolongOnSuccess) prolong() } return result } diff --git a/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt b/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt new file mode 100644 index 0000000..a4d85ee --- /dev/null +++ b/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt @@ -0,0 +1,68 @@ +package dev.inmo.krontab + +import android.content.Context +import androidx.work.* + +const val krontabTemplateWorkField = "krontabTemplate" + +/** + * Will [enqueueKronSchedulerWork] with [KronScheduler] from [krontabTemplate] and call [setUpRequest] on setting up + * [OneTimeWorkRequest.Builder] with [Data] which will be used to [OneTimeWorkRequest.Builder.setInputData] after + * [setUpRequest] completed + */ +suspend inline fun Context.enqueueKrontabTemplateSchedulerWork( + workName: String, + krontabTemplate: KrontabTemplate, + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.( + data: Data + ) -> Unit = {} +) = enqueueKronSchedulerWork(workName, krontabTemplate.toKronScheduler(), T::class.java, existingWorkPolicy) { + val data = workDataOf( + krontabTemplateWorkField to krontabTemplate + ) + setUpRequest(data) + setInputData(data) +} + +/** + * Extend this class in case you wish to base on [KrontabTemplate]. It will automatically handle request of + * [kronScheduler] and put it in [setUpRequest] + */ +abstract class KrontabTemplateSchedulerWork( + context: Context, + workerParams: WorkerParameters +) : KronSchedulerWork(context, workerParams) { + /** + * Will try to get [KrontabTemplate] from [getInputData] by key [krontabTemplateWorkField] + * + * @see setUpRequest + */ + protected val krontabTemplate: KrontabTemplate? + get() = inputData.getString(krontabTemplateWorkField) + + /** + * Override this methods instead of old [setUpRequest] in case you wish to set up some work request parameters + * + * @param data This parameter will be used to put data inside of [OneTimeWorkRequest.Builder] after this method + * will be completed + */ + protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest(data: Data) {} + + /** + * Will automatically put [krontabTemplate] into work data, call [setUpRequest] with future [Data] object and then + * call [OneTimeWorkRequest.Builder.setInputData] with that [Data] object + */ + override suspend fun OneTimeWorkRequest.Builder.setUpRequest() { + val data = workDataOf( + krontabTemplateWorkField to krontabTemplate, + ) + setUpRequest(data) + setInputData(data) + } + + /** + * Will return [KronScheduler] in case if [krontabTemplate] was not null + */ + override suspend fun kronScheduler(): KronScheduler? = krontabTemplate ?.toKronScheduler() +} From 726824fb4f9db495d373f90b51be66baf822adb6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 3 Jan 2021 12:52:04 +0600 Subject: [PATCH 9/9] disable jetifier --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 78ca93d..08a17e7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.incremental=true kotlin.incremental.js=true kotlin.incremental.multiplatform=true android.useAndroidX=true -android.enableJetifier=true +android.enableJetifier=false kotlin_version=1.4.21