mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2024-11-22 16:23:55 +00:00
commit
fd842b17fa
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.0 Years
|
||||||
|
|
||||||
|
**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
|
||||||
|
* **`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
|
||||||
|
* **Android target has been added**
|
||||||
|
|
||||||
## 0.4.2
|
## 0.4.2
|
||||||
|
|
||||||
* Versions
|
* Versions
|
||||||
|
22
README.md
22
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
|
Developers can use more simple way to configure repeat times is string. String configuring
|
||||||
like a `crontab`, but with a little bit different meanings:
|
like a `crontab`, but with a little bit different meanings:
|
||||||
```
|
```
|
||||||
/-------- Seconds
|
/---------- Seconds
|
||||||
| /------ Minutes
|
| /-------- Minutes
|
||||||
| | /---- Hours
|
| | /------ Hours
|
||||||
| | | /-- Days of months
|
| | | /---- Days of months
|
||||||
| | | | / Months
|
| | | | /-- Months
|
||||||
| | | | |
|
| | | | | / (optional) Year
|
||||||
* * * * *
|
* * * * * *
|
||||||
```
|
```
|
||||||
|
|
||||||
It is different with original `crontab` syntax for the reason, that expected that in practice developers
|
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
|
will use seconds and minutes with more probability than months (for example) or even years. In fact, developers will use
|
||||||
like:
|
something like:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
doWhile("/5 * * * *") {
|
doWhile("/5 * * * *") {
|
||||||
@ -73,7 +73,7 @@ doWhile("/5 * * * *") {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or more version:
|
An other version:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
doInfinity("/5 * * * *") {
|
doInfinity("/5 * * * *") {
|
||||||
@ -85,7 +85,7 @@ Both of examples will print `Called` message every five seconds.
|
|||||||
|
|
||||||
### Config via builder
|
### 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
|
```kotlin
|
||||||
val kronScheduler = buildSchedule {
|
val kronScheduler = buildSchedule {
|
||||||
|
60
build.gradle
60
build.gradle
@ -3,11 +3,14 @@ buildscript {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_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.kotlin.multiplatform" version "$kotlin_version"
|
||||||
id "org.jetbrains.dokka" version "$dokka_version"
|
id "org.jetbrains.dokka" version "$dokka_version"
|
||||||
}
|
}
|
||||||
|
apply plugin: "com.android.library"
|
||||||
|
|
||||||
project.version = "0.4.2"
|
project.version = "$version"
|
||||||
project.group = "dev.inmo"
|
project.group = "dev.inmo"
|
||||||
|
|
||||||
apply from: "publish.gradle"
|
apply from: "publish.gradle"
|
||||||
@ -27,6 +31,7 @@ repositories {
|
|||||||
jcenter()
|
jcenter()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: './dokka.gradle'
|
apply from: './dokka.gradle'
|
||||||
@ -37,6 +42,9 @@ kotlin {
|
|||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
android {
|
||||||
|
publishAllLibraryVariants()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -48,6 +56,13 @@ kotlin {
|
|||||||
api "com.soywiz.korlibs.klock:klock:$klockVersion"
|
api "com.soywiz.korlibs.klock:klock:$klockVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api "androidx.work:work-runtime-ktx:$androidx_work_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commonTest {
|
commonTest {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-common')
|
implementation kotlin('test-common')
|
||||||
@ -64,5 +79,48 @@ kotlin {
|
|||||||
implementation kotlin('test-js')
|
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'
|
||||||
|
test {
|
||||||
|
java.srcDir file("src/jvmTest")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ kotlin.js.generate.externals=true
|
|||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
kotlin.incremental.multiplatform=true
|
kotlin.incremental.multiplatform=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=false
|
||||||
|
|
||||||
|
|
||||||
kotlin_version=1.4.21
|
kotlin_version=1.4.21
|
||||||
@ -13,5 +15,24 @@ dokka_version=1.4.20
|
|||||||
|
|
||||||
klockVersion=2.0.3
|
klockVersion=2.0.3
|
||||||
|
|
||||||
|
## Github reease
|
||||||
|
|
||||||
github_release_plugin_version=2.2.12
|
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
|
||||||
|
|
||||||
|
@ -7,9 +7,13 @@ import kotlinx.coroutines.delay
|
|||||||
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
|
* 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!!! 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 <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
|
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
|
||||||
delay((next() - DateTime.now()).millisecondsLong)
|
next() ?.let {
|
||||||
|
delay((it - DateTime.now()).millisecondsLong)
|
||||||
|
}
|
||||||
return block()
|
return block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.krontab
|
package dev.inmo.krontab
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
|
import dev.inmo.krontab.internal.toNearDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface was created for abstraction of [next] operation. Currently, there is only
|
* This interface was created for abstraction of [next] operation. Currently, there is only
|
||||||
@ -17,5 +18,10 @@ interface KronScheduler {
|
|||||||
*
|
*
|
||||||
* @see dev.inmo.krontab.internal.CronDateTimeScheduler.next
|
* @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.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively)
|
||||||
|
suspend fun KronScheduler.nextOrNow(): DateTime = DateTime.now().let {
|
||||||
|
next(it) ?: getAnyNext(it)
|
||||||
}
|
}
|
||||||
|
@ -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<KronScheduler>.merge(): KronScheduler {
|
||||||
|
val cronDateTimes = mutableListOf<CronDateTime>()
|
||||||
|
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<KronScheduler>.merge(): KronScheduler = iterator().merge()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Vararg shortcut for [merge]
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.iterator().merge()
|
@ -0,0 +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)
|
@ -1,12 +1,15 @@
|
|||||||
package dev.inmo.krontab
|
package dev.inmo.krontab
|
||||||
|
|
||||||
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.builder.buildSchedule
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.CronDateTime
|
import dev.inmo.krontab.internal.CronDateTime
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||||
|
|
||||||
internal val anyCronDateTime by lazy {
|
internal val anyCronDateTime by lazy {
|
||||||
CronDateTime()
|
CronDateTime()
|
||||||
}
|
}
|
||||||
|
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||||
@ -48,4 +51,11 @@ val EveryDayOfMonthScheduler: KronScheduler by lazy {
|
|||||||
*/
|
*/
|
||||||
val EveryMonthScheduler: KronScheduler by lazy {
|
val EveryMonthScheduler: KronScheduler by lazy {
|
||||||
buildSchedule { months { 0 every 1 } }
|
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 } }
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ typealias KrontabTemplate = String
|
|||||||
* * hours
|
* * hours
|
||||||
* * dayOfMonth
|
* * dayOfMonth
|
||||||
* * month
|
* * month
|
||||||
|
* * (optional) year
|
||||||
*
|
*
|
||||||
* And each one have next format:
|
* And each one have next format:
|
||||||
*
|
*
|
||||||
@ -37,6 +38,7 @@ typealias KrontabTemplate = String
|
|||||||
* * Hours ranges can be found in [hoursRange]
|
* * Hours ranges can be found in [hoursRange]
|
||||||
* * Days of month ranges can be found in [dayOfMonthRange]
|
* * Days of month ranges can be found in [dayOfMonthRange]
|
||||||
* * Months ranges can be found in [monthRange]
|
* * Months ranges can be found in [monthRange]
|
||||||
|
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
@ -44,20 +46,25 @@ typealias KrontabTemplate = String
|
|||||||
* * "0/5,L * * * *" for every five seconds triggering and on 59 second
|
* * "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 * * *" 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" 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
|
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||||
*/
|
*/
|
||||||
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
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 secondsParsed = parseSeconds(secondsSource)
|
||||||
val minutesParsed = parseMinutes(minutesSource)
|
val minutesParsed = parseMinutes(minutesSource)
|
||||||
val hoursParsed = parseHours(hoursSource)
|
val hoursParsed = parseHours(hoursSource)
|
||||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||||
val monthParsed = parseMonths(monthSource)
|
val monthParsed = parseMonths(monthSource)
|
||||||
|
val yearParsed = parseYears(yearSource)
|
||||||
|
|
||||||
return createKronScheduler(
|
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]
|
* Shortcut for [buildSchedule]
|
||||||
*/
|
*/
|
||||||
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut for [buildSchedule]
|
||||||
|
*/
|
||||||
|
fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this)
|
@ -21,20 +21,21 @@ class SchedulerBuilder(
|
|||||||
private var minutes: Array<Byte>? = null,
|
private var minutes: Array<Byte>? = null,
|
||||||
private var hours: Array<Byte>? = null,
|
private var hours: Array<Byte>? = null,
|
||||||
private var dayOfMonth: Array<Byte>? = null,
|
private var dayOfMonth: Array<Byte>? = null,
|
||||||
private var month: Array<Byte>? = null
|
private var month: Array<Byte>? = null,
|
||||||
|
private var year: Array<Int>? = null
|
||||||
) {
|
) {
|
||||||
private fun <T : TimeBuilder> callAndReturn(
|
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
||||||
initial: Array<Byte>?,
|
initial: Array<I>?,
|
||||||
builder: T,
|
builder: T,
|
||||||
block: T.() -> Unit
|
block: T.() -> Unit
|
||||||
): Array<Byte>? {
|
): List<I>? {
|
||||||
builder.block()
|
builder.block()
|
||||||
|
|
||||||
val builderValue = builder.build()
|
val builderValue = builder.build()
|
||||||
|
|
||||||
return initial ?.let {
|
return initial ?.let {
|
||||||
builderValue ?.let { _ ->
|
builderValue ?.let { _ ->
|
||||||
(it + builderValue).distinct().toTypedArray()
|
(it + builderValue).distinct()
|
||||||
} ?: builderValue
|
} ?: builderValue
|
||||||
} ?: builderValue
|
} ?: builderValue
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ class SchedulerBuilder(
|
|||||||
seconds,
|
seconds,
|
||||||
SecondsBuilder(),
|
SecondsBuilder(),
|
||||||
block
|
block
|
||||||
)
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +59,7 @@ class SchedulerBuilder(
|
|||||||
minutes,
|
minutes,
|
||||||
MinutesBuilder(),
|
MinutesBuilder(),
|
||||||
block
|
block
|
||||||
)
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +70,7 @@ class SchedulerBuilder(
|
|||||||
hours,
|
hours,
|
||||||
HoursBuilder(),
|
HoursBuilder(),
|
||||||
block
|
block
|
||||||
)
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +81,7 @@ class SchedulerBuilder(
|
|||||||
dayOfMonth,
|
dayOfMonth,
|
||||||
DaysOfMonthBuilder(),
|
DaysOfMonthBuilder(),
|
||||||
block
|
block
|
||||||
)
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +92,18 @@ class SchedulerBuilder(
|
|||||||
month,
|
month,
|
||||||
MonthsBuilder(),
|
MonthsBuilder(),
|
||||||
block
|
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.createSimpleScheduler
|
||||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
* @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)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ import dev.inmo.krontab.utils.clamp
|
|||||||
* This class was created for incapsulation of builder work with specified [restrictionsRange]. For example,
|
* 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]
|
* [include] function of [TimeBuilder] will always [clamp] incoming data using its [restrictionsRange]
|
||||||
*/
|
*/
|
||||||
sealed class TimeBuilder (
|
sealed class TimeBuilder<T : Number> (
|
||||||
private val restrictionsRange: IntRange
|
private val restrictionsRange: IntRange,
|
||||||
|
private val converter: Converter<T>
|
||||||
) {
|
) {
|
||||||
private var result: Set<Int>? = null
|
private var result: Set<Int>? = null
|
||||||
|
|
||||||
@ -119,11 +120,12 @@ sealed class TimeBuilder (
|
|||||||
*/
|
*/
|
||||||
fun includeFirst() = at(restrictionsRange.first)
|
fun includeFirst() = at(restrictionsRange.first)
|
||||||
|
|
||||||
internal fun build() = result ?.map { it.toByte() } ?.toTypedArray()
|
internal fun build() = result ?.map(converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SecondsBuilder : TimeBuilder(secondsRange)
|
class SecondsBuilder : TimeBuilder<Byte>(secondsRange, intToByteConverter)
|
||||||
class MinutesBuilder : TimeBuilder(minutesRange)
|
class MinutesBuilder : TimeBuilder<Byte>(minutesRange, intToByteConverter)
|
||||||
class HoursBuilder : TimeBuilder(hoursRange)
|
class HoursBuilder : TimeBuilder<Byte>(hoursRange, intToByteConverter)
|
||||||
class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange)
|
class DaysOfMonthBuilder : TimeBuilder<Byte>(dayOfMonthRange, intToByteConverter)
|
||||||
class MonthsBuilder : TimeBuilder(monthRange)
|
class MonthsBuilder : TimeBuilder<Byte>(monthRange, intToByteConverter)
|
||||||
|
class YearsBuilder : TimeBuilder<Int>(yearRange, intToIntConverter)
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package dev.inmo.krontab.collection
|
package dev.inmo.krontab.collection
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.*
|
||||||
import dev.inmo.krontab.anyCronDateTime
|
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||||
import dev.inmo.krontab.internal.merge
|
|
||||||
import dev.inmo.krontab.internal.toNearDateTime
|
import dev.inmo.krontab.internal.toNearDateTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +18,7 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
* Add [kronScheduler] into its [schedulers] list
|
* Add [kronScheduler] into its [schedulers] list
|
||||||
*
|
*
|
||||||
* * When [kronScheduler] is [CronDateTimeScheduler] it will merge all [CronDateTimeScheduler]s from [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]
|
* * When [kronScheduler] is [CollectionKronScheduler] it this instance will include all [kronScheduler]
|
||||||
* [schedulers]
|
* [schedulers]
|
||||||
* * Otherwise [kronScheduler] will be added to [schedulers] list
|
* * Otherwise [kronScheduler] will be added to [schedulers] list
|
||||||
@ -37,7 +35,7 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
schedulers.add(
|
schedulers.add(
|
||||||
merge(resultCronDateTimes)
|
mergeCronDateTimeSchedulers(resultCronDateTimes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||||
@ -48,6 +46,6 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun next(relatively: DateTime): DateTime {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import dev.inmo.krontab.KronScheduler
|
|||||||
* @param seconds 0-59
|
* @param seconds 0-59
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTime(
|
internal data class CronDateTime(
|
||||||
|
val year: Int? = null,
|
||||||
val month: Byte? = null,
|
val month: Byte? = null,
|
||||||
val dayOfMonth: Byte? = null,
|
val dayOfMonth: Byte? = null,
|
||||||
val hours: Byte? = null,
|
val hours: Byte? = null,
|
||||||
@ -19,6 +20,7 @@ internal data class CronDateTime(
|
|||||||
val seconds: Byte? = null
|
val seconds: Byte? = null
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
|
check(year ?.let { it in yearRange } ?: true)
|
||||||
check(month ?.let { it in monthRange } ?: true)
|
check(month ?.let { it in monthRange } ?: true)
|
||||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
||||||
check(hours?.let { it in hoursRange } ?: 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]
|
* @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
|
var current = relativelyTo
|
||||||
|
|
||||||
seconds?.let {
|
seconds?.let {
|
||||||
@ -63,7 +65,13 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()
|
|||||||
|
|
||||||
month ?.let {
|
month ?.let {
|
||||||
val left = it - current.month0
|
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
|
return current
|
||||||
@ -77,7 +85,8 @@ internal fun createKronScheduler(
|
|||||||
minutes: Array<Byte>? = null,
|
minutes: Array<Byte>? = null,
|
||||||
hours: Array<Byte>? = null,
|
hours: Array<Byte>? = null,
|
||||||
dayOfMonth: Array<Byte>? = null,
|
dayOfMonth: Array<Byte>? = null,
|
||||||
month: Array<Byte>? = null
|
month: Array<Byte>? = null,
|
||||||
|
years: Array<Int>? = null
|
||||||
): KronScheduler {
|
): KronScheduler {
|
||||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||||
|
|
||||||
@ -101,5 +110,9 @@ internal fun createKronScheduler(
|
|||||||
previousCronDateTime.copy(month = currentTime)
|
previousCronDateTime.copy(month = currentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
||||||
|
previousCronDateTime.copy(year = currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.*
|
||||||
import dev.inmo.krontab.anyCronDateTime
|
import dev.inmo.krontab.collection.plus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cron-oriented realisation of [KronScheduler]
|
* Cron-oriented realisation of [KronScheduler]
|
||||||
@ -13,40 +13,44 @@ import dev.inmo.krontab.anyCronDateTime
|
|||||||
* @see dev.inmo.krontab.EveryHourScheduler
|
* @see dev.inmo.krontab.EveryHourScheduler
|
||||||
* @see dev.inmo.krontab.EveryDayOfMonthScheduler
|
* @see dev.inmo.krontab.EveryDayOfMonthScheduler
|
||||||
* @see dev.inmo.krontab.EveryMonthScheduler
|
* @see dev.inmo.krontab.EveryMonthScheduler
|
||||||
|
* @see dev.inmo.krontab.EveryYearScheduler
|
||||||
*
|
*
|
||||||
* @see dev.inmo.krontab.builder.buildSchedule
|
* @see dev.inmo.krontab.builder.buildSchedule
|
||||||
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
||||||
*/
|
*/
|
||||||
@Deprecated("This class will get internal status in future")
|
internal data class CronDateTimeScheduler internal constructor(
|
||||||
data class CronDateTimeScheduler internal constructor(
|
|
||||||
internal val cronDateTimes: List<CronDateTime>
|
internal val cronDateTimes: List<CronDateTime>
|
||||||
) : KronScheduler {
|
) : 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
|
* @see toNearDateTime
|
||||||
*/
|
*/
|
||||||
override suspend fun next(relatively: DateTime): DateTime {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
||||||
* @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of
|
schedulers.flatMap { it.cronDateTimes }
|
||||||
* [kronDateTimeSchedulers] included
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
fun merge(kronDateTimeSchedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
|
||||||
kronDateTimeSchedulers.flatMap { it.cronDateTimes }.distinct()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Vararg shortcyut for [merge]
|
* @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of
|
||||||
|
* [kronSchedulers] included
|
||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
|
||||||
inline fun merge(vararg kronDateTimeSchedulers: CronDateTimeScheduler) = merge(kronDateTimeSchedulers.toList())
|
fun merge(kronSchedulers: List<KronScheduler>) = kronSchedulers.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")
|
@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 merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.apply { dev.inmo.krontab.merge() }
|
||||||
|
/**
|
||||||
|
* @return Vararg shortcut for [dev.inmo.krontab.merge]
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
|
||||||
|
inline fun KronScheduler.plus(other: KronScheduler) = this + other
|
||||||
|
@ -2,7 +2,11 @@ package dev.inmo.krontab.internal
|
|||||||
|
|
||||||
import dev.inmo.krontab.utils.clamp
|
import dev.inmo.krontab.utils.clamp
|
||||||
|
|
||||||
private fun createSimpleScheduler(from: String, dataRange: IntRange): Array<Byte>? {
|
typealias Converter<T> = (Int) -> T
|
||||||
|
|
||||||
|
internal val intToByteConverter: Converter<Byte> = { it: Int -> it.toByte() }
|
||||||
|
internal val intToIntConverter: Converter<Int> = { it: Int -> it }
|
||||||
|
private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataConverter: Converter<T>): List<T>? {
|
||||||
val things = from.split(",")
|
val things = from.split(",")
|
||||||
|
|
||||||
val results = things.flatMap {
|
val results = things.flatMap {
|
||||||
@ -31,18 +35,19 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange): Array<Byte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.map { it.toByte() }.toTypedArray()
|
return results.map(dataConverter)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange)
|
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
||||||
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange)
|
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange)
|
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange)
|
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange)
|
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange, intToByteConverter) ?.toTypedArray()
|
||||||
|
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange, intToByteConverter) ?.toTypedArray()
|
||||||
|
|
||||||
internal fun Array<Byte>.fillWith(
|
internal fun <T> Array<T>.fillWith(
|
||||||
whereToPut: MutableList<CronDateTime>,
|
whereToPut: MutableList<CronDateTime>,
|
||||||
createFactory: (CronDateTime, Byte) -> CronDateTime
|
createFactory: (CronDateTime, T) -> CronDateTime
|
||||||
) {
|
) {
|
||||||
val previousValues = whereToPut.toList()
|
val previousValues = whereToPut.toList()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE
|
||||||
internal val monthRange = 0 .. 11
|
internal val monthRange = 0 .. 11
|
||||||
internal val dayOfMonthRange = 0 .. 30
|
internal val dayOfMonthRange = 0 .. 30
|
||||||
internal val hoursRange = 0 .. 23
|
internal val hoursRange = 0 .. 23
|
||||||
|
@ -17,7 +17,7 @@ class SchedulerFlow(
|
|||||||
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
|
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
|
||||||
while (true) {
|
while (true) {
|
||||||
val now = DateTime.now()
|
val now = DateTime.now()
|
||||||
val nextTime = scheduler.next(now)
|
val nextTime = scheduler.next(now) ?: break
|
||||||
val sleepDelay = (nextTime - now).millisecondsLong
|
val sleepDelay = (nextTime - now).millisecondsLong
|
||||||
delay(sleepDelay)
|
delay(sleepDelay)
|
||||||
collector.emit(nextTime)
|
collector.emit(nextTime)
|
||||||
|
1
src/main/AndroidManifest.xml
Normal file
1
src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.krontab"/>
|
147
src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt
Normal file
147
src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package dev.inmo.krontab
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
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 <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||||
|
workName: String,
|
||||||
|
delayMillis: Long,
|
||||||
|
workClass: Class<T>,
|
||||||
|
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||||
|
setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||||
|
) = WorkManager.getInstance(applicationContext).enqueueUniqueWork(
|
||||||
|
workName,
|
||||||
|
existingWorkPolicy,
|
||||||
|
OneTimeWorkRequest.Builder(workClass).apply {
|
||||||
|
setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
|
||||||
|
setUpRequest()
|
||||||
|
}.build()
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T] parameter
|
||||||
|
*/
|
||||||
|
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||||
|
workName: String,
|
||||||
|
delayMillis: Long,
|
||||||
|
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 <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||||
|
workName: String,
|
||||||
|
initialScheduler: KronScheduler,
|
||||||
|
workClass: Class<T>,
|
||||||
|
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, existingWorkPolicy, setUpRequest)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T]
|
||||||
|
*/
|
||||||
|
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||||
|
workName: String,
|
||||||
|
initialScheduler: KronScheduler,
|
||||||
|
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||||
|
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||||
|
) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, existingWorkPolicy, setUpRequest)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
workerParams: WorkerParameters
|
||||||
|
) : CoroutineWorker(
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
applicationContext.enqueueKronSchedulerWork(
|
||||||
|
workName,
|
||||||
|
kronScheduler() ?: return,
|
||||||
|
this::class.java
|
||||||
|
) {
|
||||||
|
setUpRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
val result = try {
|
||||||
|
onWork()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (prolongOnException) {
|
||||||
|
prolong()
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
when (result) {
|
||||||
|
is Result.Failure -> if (prolongOnFailure) prolong()
|
||||||
|
is Result.Success -> if (prolongOnSuccess) prolong()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -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 <reified T : KrontabTemplateSchedulerWork> 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()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user