Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
596e866330 Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v1.8.20 2023-04-24 18:15:55 +00:00
52 changed files with 267 additions and 914 deletions

View File

@@ -10,7 +10,10 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 17
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && cp d8 dx && cd lib && cp d8.jar dx.jar
- name: Build
run: ./gradlew dokkaHtml
- name: Publish KDocs

View File

@@ -7,13 +7,18 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 17
java-version: 11
- name: Update version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
sed -i -e "s/^version=\([0-9\.]*\)/version=\1-branch_$branch-build${{ github.run_number }}/" gradle.properties
- name: prebuild
run: ./gradlew clean build
- name: Publish to Gitea
continue-on-error: true
run: ./gradlew publishAllPublicationsToGiteaRepository
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- name: Publish package
continue-on-error: true
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.idea
.kotlin
out/*
*.iml
target

View File

@@ -1,173 +1,5 @@
# Changelog
## 2.8.0
* `Version`:
* `Kotlin`: `2.3.10`
* `Coroutines`: `1.10.2`
* `Serialization`: `1.10.0`
* `AndroidXWork`: `2.11.1`
* `Android` (compileSdk): `36`
## 2.7.2
* `Version`:
* `Kotlin`: `2.1.10`
* `Serialization`: `1.8.0`
## 2.7.1
* Fixes in parser and checker
## 2.7.0
* `Version`:
* `Kotlin`: `2.1.0`
* `Coroutines`: `1.10.1`
* Add WASM support (solution of [#137](https://github.com/InsanusMokrassar/krontab/issues/137))
## 2.6.1
* Fixes in parsing of string parts
## 2.6.0
* Fix of `doOnceTz` behaviour: now it will use local time as utc time to get next waking up time
## 2.5.1
* Add cleaning up of incoming template, which must remove all malformed parts from string template (fix of [#126](https://github.com/InsanusMokrassar/krontab/issues/126))
* Add support of insufficient amount of arguments (fix of [#126](https://github.com/InsanusMokrassar/krontab/issues/126))
* `Version`:
* `Kotlin`: `2.0.21`
* `AndroidXWork`: `2.10.0`
## 2.5.0
* `Version`:
* `Kotlin`: `2.0.20`
* `Coroutines`: `1.9.0`
* `Serialization`: `1.7.3`
## 2.4.0
* `Version`:
* `Kotlin`: `2.0.10`
* `Coroutines`: `1.8.1`
* `Serialization`: `1.7.1`
* `Klock`: `5.4.0`
* `AndroidXWork`: `2.9.1`
## 2.3.0
* `doWhile` now will guarantee that it will not call `doOnce` more than once for time
## 2.2.9
* `Version`:
* `Kotlin`: `1.9.23`
* `Serialization`: `1.6.3`
* `Klock`: `5.3.2`
* Fixes in build-in schedulers
## 2.2.8
* `Version`:
* `Coroutines`: `1.8.0`
## 2.2.7
* `Version`:
* `Klock`: `5.3.1`
* `AndroidXWork`: `2.9.0`
## 2.2.6
**Since this version klock depdendency replaced with `com.soywiz.korge:korlibs-time`**
* `Version`:
* `Klock`: `5.3.0`
## 2.2.5
* `Versions`
* `Kotlin`: `1.9.22`
* `Serialization`: `1.6.2`
## 2.2.4
* `Versions`
* `Kotlin`: `1.9.21`
* `Serialization`: `1.6.1`
## 2.2.3
**THIS UPDATE CONTAINS UPGRADE OF USED JDK UP TO 17**
* Versions
* `Kotlin`: `1.9.20`
## 2.2.2
* Versions
* `Kotlin`: `1.9.20-RC`
* `Android` (min/target): `34`
## 2.2.1
* Potential fix of [#65](https://github.com/InsanusMokrassar/krontab/issues/65)
* Versions
* `Kotlin`: `1.9.20-Beta`
* `Klock`: `4.0.10`
* `Serialization`: `1.6.0`
## 2.2.0
* Versions
* `Kotlin`: `1.9.0`
* `Coroutines`: `1.7.3`
* `Klock`: `4.0.9`
## 2.1.2
* Versions
* `Coroutines`: `1.7.2`
* All callbacks for standard extensions like `doInfinity` now fully inline
## 2.1.1
* `asFlowWithoutDelays` and `asTzFlowWithoutDelays` will have nullable `since` parameters with default to `null`
to avoid any inconsistency of `Flow` idiom.
<details>
<summary>About the reason of changes</summary>
Cold flows should not contain some state by default. So, it was not right to save some `DateTime`/`DateTimeTz`
by default. Now it will not use some external state unless developers will set it manually
</details>
## 2.1.0
* Versions
* `Kotlin`: `1.8.22`
* `Klock`: `4.0.3`
* New value class `KrontabConfig`. It is preferable way to create `KronScheduler` instead of
`KrontabTemplate` since this update
* You may configure krontab with builders using simple `KronScheduler` invoke extension
* New useful extensions like `KronScheduler.daily`
* `KrontabTemplateWrapper` is obsolete in favor to `KrontabConfig`
## 2.0.0
* Versions
* `Kotlin`: `1.8.21`
* `Klock`: `4.0.1`
* Support of `mingwx64` platform
## 1.0.0
* Versions
* `Kotlin`: `1.8.20`
* `AndroidXWork`: `2.8.1`
## 0.10.0
* New extensions for `KronScheduler`:

View File

@@ -8,10 +8,9 @@
![Android](https://img.shields.io/badge/Android-green?style=for-the-badge&logo=android&logoColor=white)
![Js](https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E)
![Linux x64](https://img.shields.io/badge/Linux%20x64-white?style=for-the-badge&logo=linux&logoColor=black)
![WASM](https://img.shields.io/badge/WASM-654FF0?style=for-the-badge&logo=webassembly&logoColor=white)
[![KDocs](https://img.shields.io/badge/KDocs-323330?style=for-the-badge&logo=Kotlin&logoColor=7F52FF)](https://insanusmokrassar.github.io/krontab/)
[![Tutorials](https://img.shields.io/badge/Tutorials-0288D1?style=for-the-badge&logo=mkdocs&logoColor=white)](https://docs.inmo.dev/krontab/index.html)
[![Tutorials](https://img.shields.io/badge/Tutorials-0288D1?style=for-the-badge&logo=bookstack&logoColor=white)](https://bookstack.inmo.dev/books/krontab)
Library was created to give oppotunity to launch some things from time to time according to some schedule in
runtime of applications.

View File

@@ -8,16 +8,14 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$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:$android_gradle_version"
classpath 'com.android.tools.build:gradle:7.3.1'
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
id "org.jetbrains.dokka" version "$dokka_version"
}
@@ -56,20 +54,10 @@ kotlin {
browser()
nodejs()
}
androidTarget {
publishLibraryVariants(
"release",
"debug",
)
android {
publishAllLibraryVariants()
}
linuxX64()
mingwX64()
linuxArm64()
wasm {
browser()
nodejs()
d8()
}
sourceSets {
@@ -77,9 +65,8 @@ kotlin {
dependencies {
implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlin_serialization_version"
api "com.soywiz.korge:korlibs-time:$klockVersion"
api "com.soywiz.korlibs.klock:klock:$klockVersion"
}
}
@@ -93,7 +80,6 @@ kotlin {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
implementation kotlin('test')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
}
}
@@ -107,7 +93,7 @@ kotlin {
implementation kotlin('test-js')
}
}
androidUnitTest {
androidTest {
dependencies {
implementation kotlin('test-junit')
}
@@ -118,9 +104,8 @@ kotlin {
apply plugin: 'com.getkeepsafe.dexcount'
android {
compileSdk "$android_compileSdkVersion".toInteger()
buildToolsVersion = "$android_buildToolsVersion"
namespace "${group}.${project.name}"
compileSdkVersion "$android_compileSdkVersion".toInteger()
buildToolsVersion "$android_buildToolsVersion"
defaultConfig {
minSdkVersion "$android_minSdkVersion".toInteger()
@@ -138,8 +123,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
@@ -151,6 +136,6 @@ android {
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

0
changelog_parser.sh Executable file → Normal file
View File

View File

@@ -11,28 +11,20 @@ private String getCurrentVersionChangelog(String version) {
return changelogDataOS.toString().trim()
}
def githubTokenVariableName = "GITHUB_RELEASE_TOKEN"
def githubTokenVariableFromEnv = System.getenv(githubTokenVariableName)
def secretFile = new File(projectDir, "secret.gradle")
if (secretFile.exists() || project.hasProperty(githubTokenVariableName) || (githubTokenVariableFromEnv != "" && githubTokenVariableFromEnv != null)) {
if (secretFile.exists()) {
apply from: './secret.gradle'
}
if (new File(projectDir, "secret.gradle").exists()) {
apply from: './secret.gradle'
apply plugin: "com.github.breadmoirai.github-release"
def githubReleaseToken = project.hasProperty(githubTokenVariableName) ? project.property(githubTokenVariableName).toString() : githubTokenVariableFromEnv
githubRelease {
token githubReleaseToken
token "${project.property('GITHUB_RELEASE_TOKEN')}"
owner = "InsanusMokrassar"
repo = "${rootProject.name}"
owner "InsanusMokrassar"
repo "${rootProject.name}"
tagName = "v${project.version}"
releaseName = "${project.version}"
targetCommitish = "${project.version}"
tagName "v${project.version}"
releaseName "${project.version}"
targetCommitish "${project.version}"
body = getCurrentVersionChangelog("${project.version}")
body getCurrentVersionChangelog("${project.version}")
}
}

View File

@@ -9,32 +9,30 @@ android.useAndroidX=true
android.enableJetifier=false
kotlin_version=2.3.10
kotlin_coroutines_version=1.10.2
kotlin_serialization_version=1.10.0
kotlin_version=1.8.20
kotlin_coroutines_version=1.6.4
dokka_version=2.1.0
dokka_version=1.8.10
klockVersion=5.4.0
klockVersion=3.4.0
## Github reease
github_release_plugin_version=2.5.2
github_release_plugin_version=2.4.1
## Android
android_gradle_version=8.12.+
android_minSdkVersion=21
android_compileSdkVersion=36
android_buildToolsVersion=36.0.0
dexcount_version=4.0.0
junit_version=4.13.2
test_ext_junit_version=1.3.0
espresso_core=3.7.0
android_compileSdkVersion=33
android_buildToolsVersion=33.0.1
dexcount_version=3.1.0
junit_version=4.12
test_ext_junit_version=1.1.3
espresso_core=3.4.0
androidx_work_version=2.11.1
androidx_work_version=2.8.0
## Common
version=2.8.0
android_code_version=49
version=0.10.0
android_code_version=24

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip

View File

@@ -1 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://github.com/InsanusMokrassar/krontab","vcsUrl":"https://github.com/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/krontab"},{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://github.com/InsanusMokrassar/krontab","vcsUrl":"https://github.com/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/krontab"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -1,7 +1,7 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
archiveClassifier = 'javadoc'
classifier = 'javadoc'
}
publishing {
@@ -50,14 +50,18 @@ publishing {
}
}
if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven {
name = "InmoNexus"
url = uri("https://nexus.inmo.dev/repository/maven-releases/")
name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials {
username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
@@ -92,27 +96,4 @@ if (project.hasProperty("signing.gnupg.keyName")) {
dependsOn(it)
}
}
// Workaround to make android sign operations depend on signing tasks
project.getTasks().withType(AbstractPublishToMaven.class).configureEach {
def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks)
}
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
}

View File

@@ -1,15 +0,0 @@
package dev.inmo.krontab
import android.annotation.SuppressLint
import androidx.work.ListenableWorker
@SuppressLint("RestrictedApi")
internal inline fun ListenableWorker.Result.checkResults(
onFailure: () -> Unit,
onSuccess: () -> Unit
) {
when (this) {
is ListenableWorker.Result.Failure -> onFailure()
is ListenableWorker.Result.Success -> onSuccess()
}
}

View File

@@ -1,176 +0,0 @@
package dev.inmo.krontab
import dev.inmo.krontab.internal.*
import dev.inmo.krontab.internal.CronDateTimeScheduler
import dev.inmo.krontab.internal.CronDateTimeSchedulerTz
import dev.inmo.krontab.internal.createKronScheduler
import dev.inmo.krontab.internal.createKronSchedulerWithOffset
import dev.inmo.krontab.internal.millisecondsArrayDefault
import dev.inmo.krontab.internal.parseDaysOfMonth
import dev.inmo.krontab.internal.parseHours
import dev.inmo.krontab.internal.parseMilliseconds
import dev.inmo.krontab.internal.parseMinutes
import dev.inmo.krontab.internal.parseMonths
import dev.inmo.krontab.internal.parseOffset
import dev.inmo.krontab.internal.parseSeconds
import dev.inmo.krontab.internal.parseWeekDay
import dev.inmo.krontab.internal.parseYears
import dev.inmo.krontab.utils.Minutes
import korlibs.time.TimezoneOffset
import korlibs.time.minutes
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
/**
* This value class contains [KrontabTemplate]
*
* * **seconds**
* * **minutes**
* * **hours**
* * **dayOfMonth**
* * **month**
* * **year** (optional)
* * **offset** (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00)
* * **dayOfWeek** (optional) (can be placed anywhere after month)
* * **milliseconds** (optional) (can be placed anywhere after month) (must be marked with `ms` at the end, for example: 500ms; 100-200ms)
*
* And each one (except of offsets) have next format:
*
* `{number}[,{number},...]` or `*`
*
* and {number} here is one of
*
* * {int}-{int}
* * {int}/{int}
* * *&#47;{int}
* * {int}
* * F
* * L
*
* Week days must be marked with `w` at the end, and starts with 0 which means Sunday. For example, 0w == Sunday. With
* weeks you can use syntax like with any number like seconds, for example: 0-2w means Sunday-Tuesday
*
* Additional info about ranges can be found in follow accordance:
*
* * Seconds ranges can be found in [secondsRange]
* * Minutes ranges can be found in [minutesRange]
* * Hours ranges can be found in [hoursRange]
* * Days of month ranges can be found in [dayOfMonthRange]
* * Months ranges can be found in [monthRange]
* * Years ranges can be found in [yearRange] (in fact - any [Int])
* * WeekDay (timezone) ranges can be found in [dayOfWeekRange]
* * Milliseconds ranges can be found in [millisecondsRange]
*
* Examples:
*
* * "0/5 * * * *" for every five seconds triggering
* * "0/5,L * * * *" for every five seconds triggering and on 59 second
* * "0/15 30 * * *" for every 15th seconds in a half of each hour
* * "0/15 30 * * * 500ms" for every 15th seconds in a half of each hour when milliseconds equal to 500
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of first, fifth and last days of may
* * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may with timezone UTC+01:00
* * "1 2 3 F,4,L 5 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may in case if it will be in Sunday-Tuesday week days with timezone UTC+01:00
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year
* * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year with timezone UTC+01:00
* * "1 2 3 F,4,L 5 2021 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00
* * "1 2 3 F,4,L 5 2021 60o 0-2w 500ms" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 when milliseconds will be equal to 500
*
* @see dev.inmo.krontab.internal.createKronScheduler
*/
@Serializable
@JvmInline
value class KrontabConfig(
@Suppress("MemberVisibilityCanBePrivate")
val template: KrontabTemplate
) {
/**
* Creates __new__ [KronScheduler] based on a [template]
*
* @return In case when offset parameter is absent in [template] will be used [createSimpleScheduler] method and
* returned [CronDateTimeScheduler]. In case when offset parameter there is in [template] [KrontabTemplate] will be used
* [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz]
*/
fun scheduler(): KronScheduler {
var offsetParsed: Int? = null
var dayOfWeekParsed: Array<Byte>? = null
var yearParsed: Array<Int>? = null
var millisecondsParsed: Array<Short>? = null
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = template
.split(" ")
.filter { checkIncomingPart(it) } // filter garbage from string
.let {
if (it.size < 5) { // reconstruction in case of insufficient arguments; 5 is amount of required arguments out of latest also code
it + (it.size until 5).map { "*" }
} else {
it
}
}
.also {
listOfNotNull(
it.getOrNull(5),
it.getOrNull(6),
it.getOrNull(7),
it.getOrNull(8)
).forEach {
val offsetFromString = parseOffset(it)
val dayOfWeekFromString = parseWeekDay(it)
val millisecondsFromString = parseMilliseconds(it)
offsetParsed = offsetParsed ?: offsetFromString
dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString
millisecondsParsed = millisecondsParsed ?: millisecondsFromString
when {
dayOfWeekFromString != null || offsetFromString != null || millisecondsFromString != null -> return@forEach
yearParsed == null -> {
yearParsed = parseYears(it)
}
}
}
}
val secondsParsed = parseSeconds(secondsSource)
val minutesParsed = parseMinutes(minutesSource)
val hoursParsed = parseHours(hoursSource)
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
val monthParsed = parseMonths(monthSource)
return offsetParsed ?.let { offset ->
createKronSchedulerWithOffset(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
TimezoneOffset(offset.minutes),
millisecondsParsed ?: millisecondsArrayDefault
)
} ?: createKronScheduler(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
millisecondsParsed ?: millisecondsArrayDefault
)
}
/**
* Creates base [KronScheduler] using [scheduler] function. In case when returned [KronScheduler] is [KronSchedulerTz],
* it will be returned as is. Otherwise, will be created new [CronDateTimeSchedulerTz] with [defaultOffset] as
* offset
*/
fun scheduler(defaultOffset: Minutes): KronSchedulerTz {
val scheduler = scheduler()
return if (scheduler is KronSchedulerTz) {
scheduler
} else {
CronDateTimeSchedulerTz(
(scheduler as CronDateTimeScheduler).cronDateTime,
TimezoneOffset(defaultOffset.minutes)
)
}
}
}

View File

@@ -1,133 +0,0 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.internal.*
internal val anyCronDateTime by lazy {
CronDateTime()
}
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now]
*/
val AnyTimeScheduler: KronScheduler by lazy {
CronDateTimeScheduler(anyCronDateTime)
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one millisecond
*/
val EveryMillisecondScheduler: KronScheduler by lazy {
buildSchedule { milliseconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one second
*/
val EverySecondScheduler: KronScheduler by lazy {
buildSchedule { seconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one minute
*/
val EveryMinuteScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one hour
*/
val EveryHourScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one day
*/
val EveryDayOfMonthScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one month
*/
val EveryMonthScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { at(0) }
months { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one year
*/
val EveryYearScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { at(0) }
months { at(0) }
years { 0 every 1 }
}
}
/**
* Shortcut for [EveryMillisecondScheduler]
*/
inline val KronScheduler.Companion.everyMillisecond
get() = EveryMillisecondScheduler
/**
* Shortcut for [EverySecondScheduler]
*/
inline val KronScheduler.Companion.everySecond
get() = EverySecondScheduler
/**
* Shortcut for [EveryMinuteScheduler]
*/
inline val KronScheduler.Companion.everyMinute
get() = EveryMinuteScheduler
/**
* Shortcut for [EveryHourScheduler]
*/
inline val KronScheduler.Companion.hourly
get() = EveryHourScheduler
/**
* Shortcut for [EveryDayOfMonthScheduler]
*/
inline val KronScheduler.Companion.daily
get() = EveryDayOfMonthScheduler
/**
* Shortcut for [EveryMonthScheduler]
*/
inline val KronScheduler.Companion.monthly
get() = EveryMonthScheduler
/**
* Shortcut for [EveryYearScheduler]
*/
inline val KronScheduler.Companion.annually
get() = EveryYearScheduler

View File

@@ -1,8 +1,7 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import korlibs.time.millisecondsLong
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlin.coroutines.coroutineContext
@@ -15,7 +14,7 @@ import kotlin.coroutines.coroutineContext
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
suspend inline fun <T> KronScheduler.doOnce(block: (DateTime) -> T): T {
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend (DateTime) -> T): T {
val time = nextOrNow().also {
delay((it - DateTime.now()).millisecondsLong)
}
@@ -30,13 +29,11 @@ suspend inline fun <T> KronScheduler.doOnce(block: (DateTime) -> T): T {
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
@Deprecated("Replaceable", ReplaceWith("doOnce", "dev.inmo.krontab.doOnce"))
suspend inline fun <T> KronScheduler.doOnceLocal(block: (DateTime) -> T): T = doOnce(block)
suspend inline fun <T> KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T = doOnce(block)
/**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! This method will use local time instead of utc
*
* 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 immediatelly
@@ -44,7 +41,7 @@ suspend inline fun <T> KronScheduler.doOnceLocal(block: (DateTime) -> T): T = do
suspend inline fun <T> KronScheduler.doOnceTz(noinline block: suspend (DateTimeTz) -> T): T {
val time = when (this) {
is KronSchedulerTz -> nextOrNowWithOffset()
else -> nextTimeZoned() ?: DateTimeTz.nowLocal()
else -> nextOrNow().local
}
delay((time - DateTimeTz.nowLocal()).millisecondsLong)
return block(time)
@@ -56,7 +53,7 @@ suspend inline fun <T> KronScheduler.doOnceTz(noinline block: suspend (DateTimeT
*/
suspend inline fun <T> doOnce(
scheduleConfig: String,
block: (DateTime) -> T
noinline block: suspend (DateTime) -> T
) = buildSchedule(scheduleConfig).doOnce(block)
/**
@@ -72,42 +69,24 @@ suspend inline fun <T> doOnceTz(
/**
* Will execute [block] while it will return true as a result of its calculation
*/
suspend inline fun KronScheduler.doWhile(block: (DateTime) -> Boolean) {
var latest: DateTime? = null
suspend inline fun KronScheduler.doWhile(noinline block: suspend (DateTime) -> Boolean) {
do {
delay(1L)
val result = doOnce {
if (latest != it) {
latest = it
block(it)
} else {
null
}
}
} while (result == null || result)
} while (doOnce(block))
}
/**
* Will execute [block] while it will return true as a result of its calculation
*/
@Deprecated("Replaceable", ReplaceWith("doWhile", "dev.inmo.krontab.doWhile"))
suspend inline fun KronScheduler.doWhileLocal(block: (DateTime) -> Boolean) = doWhile(block)
suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) = doWhile(block)
/**
* Will execute [block] while it will return true as a result of its calculation
*/
suspend inline fun KronScheduler.doWhileTz(noinline block: suspend (DateTimeTz) -> Boolean) {
var latest: DateTimeTz? = null
do {
delay(1L)
val result = doOnceTz {
if (latest != it) {
latest = it
block(it)
} else {
null
}
}
} while (result == null || result)
} while (doOnceTz(block))
}
/**
@@ -117,7 +96,7 @@ suspend inline fun KronScheduler.doWhileTz(noinline block: suspend (DateTimeTz)
*/
suspend inline fun doWhile(
scheduleConfig: String,
block: (DateTime) -> Boolean
noinline block: suspend (DateTime) -> Boolean
) = buildSchedule(scheduleConfig).doWhile(block)
/**
@@ -128,7 +107,7 @@ suspend inline fun doWhile(
@Deprecated("Replaceable", ReplaceWith("doWhile", "dev.inmo.krontab.doWhile"))
suspend inline fun doWhileLocal(
scheduleConfig: String,
block: (DateTime) -> Boolean
noinline block: suspend (DateTime) -> Boolean
) = doWhile(scheduleConfig, block)
/**
@@ -145,7 +124,7 @@ suspend inline fun doWhileTz(
/**
* Will execute [block] without any checking of result
*/
suspend inline fun KronScheduler.doInfinity(block: (DateTime) -> Unit) = doWhile {
suspend inline fun KronScheduler.doInfinity(noinline block: suspend (DateTime) -> Unit) = doWhile {
block(it)
coroutineContext.isActive
}
@@ -153,7 +132,7 @@ suspend inline fun KronScheduler.doInfinity(block: (DateTime) -> Unit) = doWhile
* Will execute [block] without any checking of result
*/
@Deprecated("Replaceable", ReplaceWith("doInfinity", "dev.inmo.krontab.doInfinity"))
suspend inline fun KronScheduler.doInfinityLocal(block: (DateTime) -> Unit) = doInfinity(block)
suspend inline fun KronScheduler.doInfinityLocal(noinline block: suspend (DateTime) -> Unit) = doInfinity(block)
/**
* Will execute [block] without any checking of result
@@ -170,7 +149,7 @@ suspend inline fun KronScheduler.doInfinityTz(noinline block: suspend (DateTimeT
*/
suspend inline fun doInfinity(
scheduleConfig: String,
block: (DateTime) -> Unit
noinline block: suspend (DateTime) -> Unit
) = buildSchedule(scheduleConfig).doInfinity(block)
/**
@@ -181,7 +160,7 @@ suspend inline fun doInfinity(
@Deprecated("Replaceable", ReplaceWith("doInfinity", "dev.inmo.krontab.doInfinity"))
suspend inline fun doInfinityLocal(
scheduleConfig: String,
block: (DateTime) -> Unit
noinline block: suspend (DateTime) -> Unit
) = doInfinity(scheduleConfig, block)
/**

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import com.soywiz.klock.DateTime
/**
* This interface was created for abstraction of [next] operation. Currently, there is only
@@ -18,8 +18,6 @@ interface KronScheduler {
* @see dev.inmo.krontab.internal.CronDateTimeScheduler.next
*/
suspend fun next(relatively: DateTime = DateTime.now()): DateTime?
companion object
}
suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively)

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
/**
* This interface extending [KronScheduler] to use [DateTimeTz] with taking into account offset of incoming time for

View File

@@ -5,10 +5,6 @@ package dev.inmo.krontab
* [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the
* hood
*/
@Deprecated(
"It is useless wrapper for KrontabTemplate. Use KrontabConfig instead",
ReplaceWith("KrontabConfig(template)", "dev.inmo.krontab.KrontabConfig")
)
data class KrontabTemplateWrapper(
val template: KrontabTemplate
) : KronScheduler by template.toKronScheduler()
@@ -19,8 +15,4 @@ data class KrontabTemplateWrapper(
* @see [toKronScheduler]
* @see [KrontabTemplateWrapper]
*/
@Deprecated(
"Will be removed in near major update with KrontabTemplateWrapper",
ReplaceWith("this.krontabConfig", "dev.inmo.krontab.krontabConfig")
)
fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this)

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import com.soywiz.klock.DateTime
class LambdaKronScheduler(
private val onNext: suspend (DateTime) -> DateTime?

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab
import korlibs.time.DateTimeTz
import com.soywiz.klock.DateTimeTz
class LambdaKronSchedulerTz(
private val onNext: suspend (DateTimeTz) -> DateTimeTz?

View File

@@ -0,0 +1,66 @@
package dev.inmo.krontab
import com.soywiz.klock.DateTime
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.internal.*
internal val anyCronDateTime by lazy {
CronDateTime()
}
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
*/
val AnyTimeScheduler: KronScheduler by lazy {
CronDateTimeScheduler(anyCronDateTime)
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one millisecond
*/
val EveryMillisecondScheduler: KronScheduler by lazy {
buildSchedule { milliseconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second
*/
val EverySecondScheduler: KronScheduler by lazy {
buildSchedule { seconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one minute
*/
val EveryMinuteScheduler: KronScheduler by lazy {
buildSchedule { minutes { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one hour
*/
val EveryHourScheduler: KronScheduler by lazy {
buildSchedule { hours { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one day
*/
val EveryDayOfMonthScheduler: KronScheduler by lazy {
buildSchedule { dayOfMonth { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one month
*/
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 } }
}

View File

@@ -1,5 +1,7 @@
package dev.inmo.krontab
import com.soywiz.klock.TimezoneOffset
import com.soywiz.klock.minutes
import dev.inmo.krontab.internal.*
import dev.inmo.krontab.utils.Minutes
@@ -9,8 +11,6 @@ import dev.inmo.krontab.utils.Minutes
*/
typealias KrontabTemplate = String
inline fun KrontabTemplate.krontabConfig() = KrontabConfig(this)
/**
* Parse [incoming] string and adapt according to next format: "* * * * *" where order of things:
*
@@ -70,16 +70,80 @@ inline fun KrontabTemplate.krontabConfig() = KrontabConfig(this)
* [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz]
*
* @see dev.inmo.krontab.internal.createKronScheduler
* @see KrontabConfig.scheduler
*/
fun createSimpleScheduler(
incoming: KrontabTemplate
): KronScheduler = KrontabConfig(incoming).scheduler()
): KronScheduler {
var offsetParsed: Int? = null
var dayOfWeekParsed: Array<Byte>? = null
var yearParsed: Array<Int>? = null
var millisecondsParsed: Array<Short>? = null
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
listOfNotNull(
it.getOrNull(5),
it.getOrNull(6),
it.getOrNull(7),
it.getOrNull(8)
).forEach {
val offsetFromString = parseOffset(it)
val dayOfWeekFromString = parseWeekDay(it)
val millisecondsFromString = parseMilliseconds(it)
offsetParsed = offsetParsed ?: offsetFromString
dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString
millisecondsParsed = millisecondsParsed ?: millisecondsFromString
when {
dayOfWeekFromString != null || offsetFromString != null || millisecondsFromString != null -> return@forEach
yearParsed == null -> {
yearParsed = parseYears(it)
}
}
}
}
val secondsParsed = parseSeconds(secondsSource)
val minutesParsed = parseMinutes(minutesSource)
val hoursParsed = parseHours(hoursSource)
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
val monthParsed = parseMonths(monthSource)
return offsetParsed ?.let { offset ->
createKronSchedulerWithOffset(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
TimezoneOffset(offset.minutes),
millisecondsParsed ?: millisecondsArrayDefault
)
} ?: createKronScheduler(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
millisecondsParsed ?: millisecondsArrayDefault
)
}
fun createSimpleScheduler(
incoming: KrontabTemplate,
defaultOffset: Minutes
): KronSchedulerTz = KrontabConfig(incoming).scheduler(defaultOffset)
): KronSchedulerTz {
val scheduler = createSimpleScheduler(incoming)
return if (scheduler is KronSchedulerTz) {
scheduler
} else {
CronDateTimeSchedulerTz(
(scheduler as CronDateTimeScheduler).cronDateTime,
TimezoneOffset(defaultOffset.minutes)
)
}
}
/**
* Shortcut for [createSimpleScheduler]

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab.builder
import korlibs.time.TimezoneOffset
import korlibs.time.minutes
import com.soywiz.klock.TimezoneOffset
import com.soywiz.klock.minutes
import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.KronSchedulerTz
import dev.inmo.krontab.internal.*
@@ -14,7 +14,7 @@ import dev.inmo.krontab.utils.Minutes
*
* @see dev.inmo.krontab.createSimpleScheduler
*/
inline fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
val builder = SchedulerBuilder()
builder.settingsBlock()
@@ -27,7 +27,7 @@ inline fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronSchedu
*
* @see dev.inmo.krontab.createSimpleScheduler
*/
inline fun buildSchedule(
fun buildSchedule(
offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz {
@@ -38,27 +38,6 @@ inline fun buildSchedule(
return builder.build() as KronSchedulerTz
}
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz = buildSchedule(offset, settingsBlock)
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
settingsBlock: SchedulerBuilder.() -> Unit
): KronScheduler = buildSchedule(settingsBlock)
class SchedulerBuilder(
private var seconds: Array<Byte>? = null,
private var minutes: Array<Byte>? = null,
@@ -182,24 +161,15 @@ class SchedulerBuilder(
*/
fun build(): KronScheduler = offset ?.let {
createKronSchedulerWithOffset(
seconds = seconds,
minutes = minutes,
hours = hours,
dayOfMonth = dayOfMonth,
month = month,
years = year,
weekDays = dayOfWeek,
offset = TimezoneOffset(it.minutes),
milliseconds = milliseconds ?: millisecondsArrayDefault
seconds,
minutes,
hours,
dayOfMonth,
month,
year,
dayOfWeek,
TimezoneOffset(it.minutes),
milliseconds ?: millisecondsArrayDefault
)
} ?: createKronScheduler(
seconds = seconds,
minutes = minutes,
hours = hours,
dayOfMonth = dayOfMonth,
month = month,
years = year,
weekDays = dayOfWeek,
milliseconds = milliseconds ?: millisecondsArrayDefault
)
} ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek, milliseconds ?: millisecondsArrayDefault)
}

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab.collection
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import dev.inmo.krontab.*
import dev.inmo.krontab.internal.*

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab.internal
import korlibs.time.DateTime
import korlibs.time.TimezoneOffset
import com.soywiz.klock.DateTime
import com.soywiz.klock.TimezoneOffset
import dev.inmo.krontab.KronScheduler
/**

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.internal
import korlibs.time.DateTime
import com.soywiz.klock.DateTime
import dev.inmo.krontab.KronScheduler
/**

View File

@@ -1,8 +1,8 @@
package dev.inmo.krontab.internal
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import korlibs.time.TimezoneOffset
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import com.soywiz.klock.TimezoneOffset
import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.KronSchedulerTz

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.internal
import korlibs.time.*
import com.soywiz.klock.*
import dev.inmo.krontab.utils.copy
import kotlin.math.min
@@ -108,19 +108,13 @@ internal fun NearDateTimeCalculatorDays(
} else {
dateTime
})
val newDateTime = dateTime.copy(
dateTime.copy(
dayOfMonth = min(dateTime.month.days(dateTime.year), newOne.toInt() + 1), // index1
hour = 0,
minute = 0,
second = 0,
milliseconds = 0
)
// If day of month has not been changed, use old dateTime due to no changes required
if (newDateTime.dayOfMonth == dateTime.dayOfMonth) {
dateTime
} else {
newDateTime
}
}
)

View File

@@ -37,106 +37,6 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
return results.map(dataConverter)
}
/**
* FSM for parsing of incoming data. If at the end of parsing it have non-null state and string is not empty, data passed check
*
* 1.
* * "\\d" -> 2
* * "\\*" -> 4
* * "F" -> 7
* * "f" -> 7
* * "L" -> 7
* * "l" -> 7
* * "/" -> 6
* 2.
* * "\\d" -> 2
* * "/" -> 6
* * "," -> 1
* * "-" -> 3
* * "m" -> 9
* * "o" -> 10
* * "w" -> 10
* 3.
* * "L" -> 7
* * "l" -> 7
* * "\\d" -> 8
* 4.
* * "/" -> 6
* * "," -> 1
* 5.
* * "/" -> 6
* 6.
* * "\\d" -> 8
* * "\\*" -> 7
* 7.
* * "," -> 1
* 8.
* * "\\d" -> 8
* * "," -> 1
* 9.
* * "s" -> 10 // end of ms
* 10. Empty, end of parse
*/
private val checkIncomingPartTransitionsMap = listOf(
listOf( // 0
Regex("\\d") to 1,
Regex("\\*") to 3,
Regex("[Ff]") to 6,
Regex("[Ll]") to 6,
Regex("/") to 5,
),
listOf( // 1
Regex("\\d") to 1,
Regex("/") to 5,
Regex(",") to 0,
Regex("-") to 2,
Regex("m") to 8,
Regex("o") to 9,
Regex("w") to 9,
),
listOf( // 2
Regex("[Ll]") to 6,
Regex("\\d") to 7,
),
listOf( // 3
Regex("/") to 5,
Regex(",") to 0,
),
listOf( // 4
Regex("/") to 5,
),
listOf( // 5
Regex("\\d") to 7,
Regex("\\*") to 6,
),
listOf( // 6
Regex(",") to 0,
),
listOf( // 7
Regex("\\d") to 7,
Regex(",") to 0,
),
listOf( // 8
Regex("s") to 9, // end of ms
),
listOf(), // empty state, end of parsing
)
internal fun checkIncomingPart(part: String): Boolean {
var i = 0
var state = checkIncomingPartTransitionsMap[0]
while (i < part.length) {
val char = part[i]
val nextState = state.firstNotNullOfOrNull {
it.second.takeIf { _ -> it.first.matches("$char") }
}
if (nextState == null) return false
state = checkIncomingPartTransitionsMap[nextState]
i++
}
return part.isNotEmpty()
}
internal fun parseWeekDay(from: String?) = from ?.let { if (it.endsWith("w")) createSimpleScheduler(it.removeSuffix("w"), dayOfWeekRange, intToByteConverter) ?.toTypedArray() else null }
internal fun parseOffset(from: String?) = from ?.let { if (it.endsWith("o")) it.removeSuffix("o").toIntOrNull() else null }
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab.utils
import korlibs.time.DateTime
import korlibs.time.Month
import com.soywiz.klock.DateTime
import com.soywiz.klock.Month
import kotlin.math.min
fun DateTime.copy(

View File

@@ -1,11 +1,10 @@
package dev.inmo.krontab.utils
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import korlibs.time.milliseconds
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import com.soywiz.klock.milliseconds
import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.next
import korlibs.time.millisecondsLong
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -19,11 +18,10 @@ import kotlinx.coroutines.isActive
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
* be completed
*
* @param since Will be used as the first parameter for [KronScheduler.next] fun. If passed null, `flow`
* will always start since the moment of collecting start
* @param since Will be used as the first parameter for [KronScheduler.next] fun
*/
fun KronScheduler.asTzFlowWithoutDelays(since: DateTimeTz? = null): Flow<DateTimeTz> = flow {
var previous = since ?: DateTime.nowLocal()
fun KronScheduler.asTzFlowWithoutDelays(since: DateTimeTz = DateTime.nowLocal()): Flow<DateTimeTz> = flow {
var previous = since
while (currentCoroutineContext().isActive) {
val next = next(previous) ?: break
emit(next)
@@ -59,11 +57,10 @@ fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = asTzFlowWithDelays()
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
* be completed
*
* @param since Will be used as the first parameter for [KronScheduler.next] fun. If passed null, `flow`
* will always start since the moment of collecting start
* @param since Will be used as the first parameter for [KronScheduler.next] fun
*/
fun KronScheduler.asFlowWithoutDelays(since: DateTime? = null): Flow<DateTime> = flow {
var previous = since ?: DateTime.now()
fun KronScheduler.asFlowWithoutDelays(since: DateTime = DateTime.now()): Flow<DateTime> = flow {
var previous = since
while (currentCoroutineContext().isActive) {
val next = next(previous) ?: break
emit(next)

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import com.soywiz.klock.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import com.soywiz.klock.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import com.soywiz.klock.*
import kotlinx.coroutines.flow.Flow
@Suppress("NOTHING_TO_INLINE")

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import com.soywiz.klock.*
import kotlinx.coroutines.flow.Flow
@Suppress("NOTHING_TO_INLINE")

View File

@@ -1,38 +0,0 @@
package dev.inmo.krontab.utils
import dev.inmo.krontab.*
import korlibs.time.*
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
class BuildersTest {
@Test
fun presetsWorksCorrectly() {
val data = mapOf(
EverySecondScheduler to { it: DateTime -> if (it.milliseconds > 0 ) it + 1.seconds - it.milliseconds.milliseconds else it },
EveryMinuteScheduler to { it: DateTime -> if (it.seconds > 0 || it.milliseconds > 0 ) it + 1.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryHourScheduler to { it: DateTime -> if (it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) it + 1.hours - it.minutes.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryDayOfMonthScheduler to { it: DateTime -> if (it.hours > 0 || it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) it + 1.days - it.hours.hours - it.minutes.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryMonthScheduler to { it: DateTime -> if (it.dayOfMonth > 1 || it.hours > 0 || it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) (it + 1.months).copy(dayOfMonth = 1, hour = 0, minute = 0, second = 0, milliseconds = 0) else it },
)
val samples = 10000
runTest {
var now = DateTime.now()
for (i in 0 until samples) {
data.forEach { (scheduler, expectCalculator) ->
val expectValue = expectCalculator(now)
val newNow = scheduler.nextOrRelative(now)
assertEquals(expectValue, newNow, "For time ${now.toStringDefault()} calculated wrong value: ${newNow.toStringDefault()} is not equal to ${expectValue.toStringDefault()}")
now = newNow
}
}
}
}
}

View File

@@ -1,7 +1,7 @@
package dev.inmo.krontab.utils
import korlibs.time.DateTime
import korlibs.time.days
import com.soywiz.klock.DateTime
import com.soywiz.klock.days
import dev.inmo.krontab.buildSchedule
import kotlinx.coroutines.test.runTest
import kotlin.test.*

View File

@@ -1,8 +1,7 @@
package dev.inmo.krontab.utils
import korlibs.time.*
import com.soywiz.klock.*
import dev.inmo.krontab.KronSchedulerTz
import dev.inmo.krontab.KrontabTemplate
import dev.inmo.krontab.buildSchedule
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.takeWhile
@@ -12,52 +11,23 @@ import kotlin.test.*
@ExperimentalCoroutinesApi
@FlowPreview
class StringParseTest {
private fun makeSimpleEverySecondTest(template: KrontabTemplate) {
val kronScheduler = buildSchedule(template)
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * *")
val flow = kronScheduler.asFlowWithoutDelays()
runTest {
val mustBeCollected = 10
var collected = 0
var previousTime: DateTime? = null
flow.takeWhile {
collected < mustBeCollected
}.collect {
previousTime ?.let { previousTime ->
assertEquals(previousTime + 1.seconds, it)
}
previousTime = it
collected++
}
assertEquals(mustBeCollected, collected)
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
makeSimpleEverySecondTest("*/1 * * * *")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithWrongAmountOfSpaces() {
val templatesFirstReplacers = listOf(
"*/1",
"*",
"/1",
"f,/1",
)
templatesFirstReplacers.forEach { replacer ->
makeSimpleEverySecondTest("$replacer * * * * ")
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithGarbageInTemplate() {
makeSimpleEverySecondTest(" sdf */1 * * * oo * ")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithInsufficientArgsInTemplate() {
makeSimpleEverySecondTest(" sdf */1 ")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondWhenMillisIsHalfOfSecondBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * * 500ms")
@@ -67,14 +37,9 @@ class StringParseTest {
runTest {
val mustBeCollected = 10
var collected = 0
var previousTime: DateTime? = null
flow.takeWhile {
collected < mustBeCollected
}.collect {
previousTime ?.let { previousTime ->
assertEquals(previousTime.copy(milliseconds = 500) + 1.seconds, it)
}
previousTime = it
collected++
}
assertEquals(mustBeCollected, collected)

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils
import korlibs.time.*
import com.soywiz.klock.*
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.next
import kotlinx.coroutines.test.runTest

View File

@@ -1,6 +1,6 @@
package dev.inmo.krontab.utils
import korlibs.time.*
import com.soywiz.klock.*
import dev.inmo.krontab.builder.buildSchedule
import kotlinx.coroutines.test.runTest
import kotlin.math.ceil

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.krontab"/>

View File

@@ -2,8 +2,7 @@ package dev.inmo.krontab
import android.content.Context
import androidx.work.*
import korlibs.time.DateTime
import korlibs.time.millisecondsLong
import com.soywiz.klock.DateTime
import java.util.concurrent.TimeUnit
/**
@@ -139,14 +138,10 @@ abstract class KronSchedulerWork(
}
throw e
}
result.checkResults(
onFailure = {
if (prolongOnFailure) prolong()
},
onSuccess = {
if (prolongOnSuccess) prolong()
}
)
when (result) {
is Result.Failure -> if (prolongOnFailure) prolong()
is Result.Success -> if (prolongOnSuccess) prolong()
}
return result
}
}