mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2025-12-05 04:25:48 +00:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8071fa15c | |||
| 1cc8ff5519 | |||
| aebd32cb5e | |||
| d11f106f19 | |||
| 36f316357f | |||
| 5fcbe50e39 | |||
| 86552d427c | |||
| 92d902c027 | |||
| 9d8cb20d10 | |||
| ca248a25ad | |||
| 4995a34c1a | |||
| 3162780447 | |||
| 05af4d1f67 | |||
| 3c0818cabf | |||
| a38c233bbf | |||
| 346672b32b | |||
| 7458e4880d | |||
| 46f227f9a3 | |||
| 35f5531d26 | |||
| 1e7acb2f4b | |||
| 50e6868fba | |||
| e0ece2de33 | |||
| 104e9b1c87 | |||
| c1d912f170 | |||
| f4ace9ac37 | |||
| 6374911902 | |||
| 7b370ef3e9 | |||
| 07f2d7b9cd | |||
| 95244dc18f | |||
| 2a8267f0c9 | |||
| 753dcae747 | |||
| 1431c0cda2 | |||
| 66e75b4315 | |||
| 5a13437c17 | |||
| 6d612ce95d | |||
| 92f1ec03dd | |||
| f23740a6a5 | |||
| 16d8850ca7 | |||
| f278361470 | |||
| 47d84751e3 | |||
| 3aa658168d | |||
| 60458999b4 | |||
| 94573ecd43 | |||
| 241fb2ee51 | |||
| cdeea96c54 | |||
| 4b310071d7 | |||
| 5ccebc5033 | |||
| 65477bb0bc | |||
| 990b0d1011 | |||
| 5cc9c8278e | |||
| 12d2d82c71 | |||
| 0d19d80d48 | |||
| 5358bfdb47 | |||
| 5c452d58b7 | |||
| a36fd1a30a | |||
| 6dc85d7e18 | |||
| 07e6baba3d | |||
| d8ed07da98 | |||
| c9243d7968 | |||
| eb11235360 | |||
| 565aff4538 | |||
| 686716e20f | |||
| 6077384a17 | |||
| 408349be04 | |||
| 237deff0e6 | |||
| 59cd600d60 | |||
| be33a130d3 | |||
| 1f971117bd | |||
| 0178ac140a | |||
| 69e2e10d29 | |||
| 25901b7ab6 | |||
| 7892a6897e | |||
| ef436892aa | |||
| 85cb87c459 | |||
| 75f6e06552 | |||
| e600d5fec9 | |||
| f480873cf0 | |||
| 363aeed732 | |||
| 18e0af4592 | |||
| 5c0e02220f | |||
| d516c26381 | |||
| 10ee1672bc | |||
| b564c36359 | |||
| 394522db6b |
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: InsanusMokrassar
|
||||
|
||||
custom: ['https://paypal.me/InsanusMokrassar?locale.x=ru_RU']
|
||||
3
.github/labeler.yml
vendored
Normal file
3
.github/labeler.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
code: "**/*.kt"
|
||||
gradle: "**/*.gradle"
|
||||
markdown: "**/*.md"
|
||||
13
.github/workflows/greetings.yml
vendored
Normal file
13
.github/workflows/greetings.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Greetings
|
||||
|
||||
on: [pull_request, issues]
|
||||
|
||||
jobs:
|
||||
greeting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Welcome with your first issue'
|
||||
pr-message: 'Welcome with your first Pull Request'
|
||||
18
.github/workflows/label.yml
vendored
Normal file
18
.github/workflows/label.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# This workflow will triage pull requests and apply a label based on the
|
||||
# paths that are modified in the pull request.
|
||||
#
|
||||
# To use this workflow, you will need to set up a .github/labeler.yml
|
||||
# file with configuration. For more information, see:
|
||||
# https://github.com/actions/labeler/blob/master/README.md
|
||||
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
10
.travis.yml
Normal file
10
.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
language: java
|
||||
install: true
|
||||
|
||||
os: linux
|
||||
dist: trusty
|
||||
jdk: oraclejdk8
|
||||
|
||||
script:
|
||||
- ./gradlew build allTests -s
|
||||
|
||||
94
CHANGELOG.md
94
CHANGELOG.md
@@ -1,12 +1,96 @@
|
||||
# Changelog
|
||||
|
||||
# 0.2.0
|
||||
## 0.4.2
|
||||
|
||||
* Versions
|
||||
* `Kotlin`: `1.4.20` -> `1.4.21`
|
||||
* `Klock`: `2.0.1` -> `2.0.3`
|
||||
* `CronDateTimeScheduler` now is deprecated and will be set up as `internal` in future
|
||||
|
||||
## 0.4.1
|
||||
|
||||
* Versions:
|
||||
* `Coroutines`: `1.4.1` -> `1.4.2`
|
||||
* `Klock`: `2.0.0` -> `2.0.1`
|
||||
* `CronDateTimeScheduler` now is public
|
||||
* New functions for `CronDateTimeScheduler`
|
||||
* Add `CollectionKronScheduler`. It will give opportunity to unite several schedulers in one
|
||||
|
||||
## 0.4.0
|
||||
|
||||
**BREAKING CHANGES**
|
||||
Package of project has been changed. Migration:
|
||||
|
||||
* Replace in your dependencies `com.insanusmokrassar:krontab` by `dev.inmo:krontab`
|
||||
* Replace in your project all imports `com.insanusmokrassar.krontab` by `dev.inmo.krontab`
|
||||
|
||||
* Versions:
|
||||
* `Kotlin`: `1.4.10` -> `1.4.20`
|
||||
* `Klock`: `1.12.1` -> `2.0.0`
|
||||
|
||||
## 0.3.3
|
||||
|
||||
* Versions:
|
||||
* `Coroutines`: `1.3.9` -> `1.4.1`
|
||||
|
||||
## 0.3.2
|
||||
|
||||
* Function `TimeBuilder#each` was added (works as `at`)
|
||||
* Add opportunity to use `first` shortcuts:
|
||||
* Value property `TimeBuilder#first` for including via functions like `TimeBuilder#at`
|
||||
* Shortcut for kron string format `f` or `F`
|
||||
* Add opportunity to use `last` shortcuts:
|
||||
* Value property `TimeBuilder#last` for including via functions like `TimeBuilder#at`
|
||||
* Shortcut for kron string format `l` or `L`
|
||||
|
||||
## 0.3.1
|
||||
|
||||
* Versions:
|
||||
* `Kotlin`: `1.4.0` -> `1.4.10`
|
||||
* `Klock`: `1.12.0` -> `1.12.1`
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Versions:
|
||||
* `Kotlin`: `1.3.72` -> `1.4.0`
|
||||
* `Coroutines`: `1.3.8` -> `1.3.9`
|
||||
* `Klock`: `1.11.14` -> `1.12.0`
|
||||
* Typealias `KrontabTemplate` was added
|
||||
* Extension `KrontabTemplate#toSchedule` was added
|
||||
|
||||
### 0.2.4
|
||||
|
||||
* Updates in libraries:
|
||||
* Klock `1.11.3` -> `1.11.14`
|
||||
* Coroutines `1.3.7` -> `1.3.8`
|
||||
* Ranges support were included. Now it is possible to correctly use syntax `0-5` in strings schedules
|
||||
|
||||
## 0.2.3
|
||||
|
||||
* Updates in libraries:
|
||||
* Kotlin `1.3.70` -> `1.3.72`
|
||||
* Coroutines `1.3.5` -> `1.3.7`
|
||||
* Klock `1.10.0` -> `1.11.3`
|
||||
* A lot of KDocs added and fixed
|
||||
* `EverySecondScheduler` changed its building logic - now it is lazy with builder using
|
||||
* `KronScheduler#doOnce` was optimized: now it will be explicitly called once and return result of its calculations
|
||||
* `KronScheduler#doWhile` was rewritten to use `KronScheduler#doOnce` for calculations of `block` result
|
||||
* New `buildSchedule(String)` function as a shortcut for `createSimpleScheduler(String)`
|
||||
|
||||
## 0.2.2
|
||||
|
||||
* Updates in libraries:
|
||||
* Kotlin `1.3.61` -> `1.3.70`
|
||||
* Coroutines `1.3.3` -> `1.3.5`
|
||||
* Klock `1.8.6` -> `1.10.0`
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Added support of flows: now any `KronScheduler` can be convert to `Flow<DateTime>` using `asFlow` extension
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Updated way of publishing (for more info look at the [git](https://git.insanusmokrassar.com/InsanusMokrassar/krontab))
|
||||
* Updates in libraries:
|
||||
* Coroutines `1.3.2` -> `1.3.3`
|
||||
* Klock `1.7.3` -> `1.8.6`
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Added support of flows: now any `KronScheduler` can be convert to `Flow<DateTime>` using `asFlow` extension
|
||||
|
||||
12
README.md
12
README.md
@@ -1,6 +1,8 @@
|
||||
# krontab
|
||||
|
||||
[  ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab/_latestVersion)
|
||||
[ ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
|
||||
[](https://travis-ci.com/InsanusMokrassar/krontab)
|
||||
|
||||
Library was created to give oppotunity to launch some things from time to time according to some schedule in
|
||||
runtime of applications.
|
||||
@@ -37,12 +39,12 @@ If you want to include `krontab` in your project, just add next line to your
|
||||
dependencies part:
|
||||
|
||||
```groovy
|
||||
implementation "com.insanusmokrassar:krontab:$krontab_version"
|
||||
implementation "dev.inmo:krontab:$krontab_version"
|
||||
```
|
||||
|
||||
Next version is the latest currently for the library:
|
||||
|
||||
[  ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab/_latestVersion)
|
||||
[  ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion)
|
||||
|
||||
For old version of Gradle, instead of `implementation` word developers must use `compile`.
|
||||
|
||||
@@ -140,8 +142,8 @@ val kronScheduler = buildSchedule {
|
||||
val flow = kronScheduler.asFlow()
|
||||
```
|
||||
|
||||
So, in this case any operations related to flow are available and it is expected tt th will work correctly. For example,
|
||||
it is possible to use this flow with `takeWhile`:
|
||||
So, in this case any operations related to flow are available and it is expected that they will work correctly. For
|
||||
example, it is possible to use this flow with `takeWhile`:
|
||||
|
||||
```kotlin
|
||||
flow.takeWhile {
|
||||
|
||||
1
_config.yml
Normal file
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
32
build.gradle
32
build.gradle
@@ -7,20 +7,20 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
|
||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
project.version = "0.2.1"
|
||||
project.group = "com.insanusmokrassar"
|
||||
project.version = "0.4.2"
|
||||
project.group = "dev.inmo"
|
||||
|
||||
apply from: "publish.gradle"
|
||||
apply from: "github_release.gradle"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -29,15 +29,21 @@ repositories {
|
||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||
}
|
||||
|
||||
apply from: './dokka.gradle'
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
js()
|
||||
js(BOTH) {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib')
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version"
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
|
||||
api "com.soywiz.korlibs.klock:klock:$klockVersion"
|
||||
}
|
||||
@@ -46,24 +52,16 @@ kotlin {
|
||||
dependencies {
|
||||
implementation kotlin('test-common')
|
||||
implementation kotlin('test-annotations-common')
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
jsTest {
|
||||
dependencies {
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$kotlin_coroutines_version"
|
||||
implementation kotlin('test-js')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
changelog_parser.sh
Normal file
24
changelog_parser.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
function parse() {
|
||||
version=$1
|
||||
|
||||
while IFS= read -r line && [ -z "`echo $line | grep -e "^#\+ $version"`" ]
|
||||
do
|
||||
: # do nothing
|
||||
done
|
||||
|
||||
while IFS= read -r line && [ -z "`echo $line | grep -e "^#\+"`" ]
|
||||
do
|
||||
echo "$line"
|
||||
done
|
||||
}
|
||||
|
||||
version=$1
|
||||
file=$2
|
||||
|
||||
if [ -n "$file" ]; then
|
||||
parse $version < "$file"
|
||||
else
|
||||
parse $version
|
||||
fi
|
||||
45
dokka.gradle
Normal file
45
dokka.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
dokkaHtml {
|
||||
String dokkaOutputDirectoryPath = null
|
||||
switch (true) {
|
||||
case project.hasProperty("DOKKA_PATH"):
|
||||
dokkaOutputDirectoryPath = project.property("DOKKA_PATH").toString()
|
||||
break
|
||||
case System.getenv("DOKKA_PATH") != null:
|
||||
dokkaOutputDirectoryPath = System.getenv("DOKKA_PATH")
|
||||
break
|
||||
}
|
||||
if (dokkaOutputDirectoryPath != null) {
|
||||
File outputDirectoryFile = new File(dokkaOutputDirectoryPath)
|
||||
outputDirectoryFile.mkdirs()
|
||||
outputDirectory = outputDirectoryFile
|
||||
}
|
||||
|
||||
moduleName.set("kdocs")
|
||||
|
||||
dokkaSourceSets {
|
||||
configureEach {
|
||||
skipDeprecated.set(true)
|
||||
includeNonPublic.set(true)
|
||||
reportUndocumented.set(true)
|
||||
|
||||
sourceLink {
|
||||
localDirectory.set(file("./"))
|
||||
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/krontab/blob/master/"))
|
||||
remoteLineSuffix.set("#L")
|
||||
}
|
||||
}
|
||||
|
||||
named("commonMain") {
|
||||
sourceRoot { path = "src/commonMain" }
|
||||
}
|
||||
|
||||
//
|
||||
// named("jsMain") {
|
||||
// sourceRoot { path = "src/jsMain" }
|
||||
// }
|
||||
//
|
||||
// named("jvmMain") {
|
||||
// sourceRoot { path = "src/jvmMain" }
|
||||
// }
|
||||
}
|
||||
}
|
||||
30
github_release.gradle
Normal file
30
github_release.gradle
Normal file
@@ -0,0 +1,30 @@
|
||||
private String getCurrentVersionChangelog(String version) {
|
||||
OutputStream changelogDataOS = new ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine 'chmod', "+x", './changelog_parser.sh'
|
||||
}
|
||||
exec {
|
||||
standardOutput = changelogDataOS
|
||||
commandLine './changelog_parser.sh', "$version", 'CHANGELOG.md'
|
||||
}
|
||||
|
||||
return changelogDataOS.toString().trim()
|
||||
}
|
||||
|
||||
if (new File(projectDir, "secret.gradle").exists()) {
|
||||
apply from: './secret.gradle'
|
||||
apply plugin: "com.github.breadmoirai.github-release"
|
||||
|
||||
githubRelease {
|
||||
token "${project.property('GITHUB_RELEASE_TOKEN')}"
|
||||
|
||||
owner "InsanusMokrassar"
|
||||
repo "${rootProject.name}"
|
||||
|
||||
tagName "${project.version}"
|
||||
releaseName "${project.version}"
|
||||
targetCommitish "${project.version}"
|
||||
|
||||
body getCurrentVersionChangelog("${project.version}")
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,17 @@
|
||||
kotlin.code.style=official
|
||||
kotlin_version=1.3.61
|
||||
kotlin_coroutines_version=1.3.3
|
||||
|
||||
gradle_bintray_plugin_version=1.8.4
|
||||
|
||||
klockVersion=1.8.6
|
||||
|
||||
org.gradle.parallel=true
|
||||
kotlin.js.generate.externals=true
|
||||
kotlin.incremental=true
|
||||
kotlin.incremental.js=true
|
||||
kotlin.incremental.multiplatform=true
|
||||
|
||||
|
||||
kotlin_version=1.4.21
|
||||
kotlin_coroutines_version=1.4.2
|
||||
|
||||
dokka_version=1.4.20
|
||||
|
||||
klockVersion=2.0.3
|
||||
|
||||
github_release_plugin_version=2.2.12
|
||||
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
# Tue May 21 17:58:54 HKT 2019
|
||||
# Fri Jul 24 23:52:00 +06 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
task javadocsJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
project.publishing.publications.all {
|
||||
// rename artifacts
|
||||
groupId "${project.group}"
|
||||
if (it.name.contains('kotlinMultiplatform')) {
|
||||
artifactId = "${project.name}"
|
||||
} else {
|
||||
artifactId = "${project.name}-$name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.all {
|
||||
artifact javadocsJar
|
||||
|
||||
pom.withXml {
|
||||
asNode().children().last() + {
|
||||
resolveStrategy = Closure.DELEGATE_FIRST
|
||||
|
||||
description "It is an analog of crontab util for Kotlin Coroutines"
|
||||
name "Krontab"
|
||||
url "https://git.insanusmokrassar.com/InsanusMokrassar/krontab"
|
||||
|
||||
scm {
|
||||
developerConnection "scm:git:[fetch=]https://git.insanusmokrassar.com/InsanusMokrassar/krontab.git[push=]https://git.insanusmokrassar.com/InsanusMokrassar/krontab.git"
|
||||
url "https://git.insanusmokrassar.com/InsanusMokrassar/krontab.git"
|
||||
}
|
||||
|
||||
developers {
|
||||
|
||||
developer {
|
||||
id "InsanusMokrassar"
|
||||
name "Ovsiannikov Aleksei"
|
||||
email "ovsyannikov.alexey95@gmail.com"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
licenses {
|
||||
|
||||
license {
|
||||
name "Apache Software License 2.0"
|
||||
url "https://git.insanusmokrassar.com/InsanusMokrassar/krontab/src/master/LICENSE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}","packageVcs":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab/src/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab","vcsUrl":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]}}
|
||||
1
mpp_config.kpsb
Normal file
1
mpp_config.kpsb
Normal file
@@ -0,0 +1 @@
|
||||
{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}-mpp","packageVcs":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab/src/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://git.insanusmokrassar.com/InsanusMokrassar/krontab","vcsUrl":"https://git.insanusmokrassar.com:8322/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]}}
|
||||
107
publish.gradle
107
publish.gradle
@@ -1,52 +1,69 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
apply from: "maven.publish.gradle"
|
||||
|
||||
ext {
|
||||
projectBintrayDir = "${project.group}/".replace(".", "/") + "${project.name}/${project.version}"
|
||||
task javadocsJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
task sourceJar (type : Jar) {
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
bintray {
|
||||
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
|
||||
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
|
||||
filesSpec {
|
||||
from "${buildDir}/publications/"
|
||||
eachFile {
|
||||
if (it.getName() == "module.json") {
|
||||
File file = it.getFile()
|
||||
String directorySubname = file.parentFile.name
|
||||
if (directorySubname == "kotlinMultiplatform") {
|
||||
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
|
||||
} else {
|
||||
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
|
||||
afterEvaluate {
|
||||
project.publishing.publications.all {
|
||||
// rename artifacts
|
||||
groupId "${project.group}"
|
||||
if (it.name.contains('kotlinMultiplatform')) {
|
||||
artifactId = "${project.name}"
|
||||
artifact sourceJar
|
||||
} else {
|
||||
artifactId = "${project.name}-$name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.all {
|
||||
artifact javadocsJar
|
||||
|
||||
pom {
|
||||
description = "It is an analog of crontab util for Kotlin Coroutines"
|
||||
name = "Krontab"
|
||||
url = "https://git.insanusmokrassar.com/InsanusMokrassar/krontab"
|
||||
|
||||
scm {
|
||||
developerConnection = "scm:git:[fetch=]https://git.insanusmokrassar.com:8322/InsanusMokrassar/krontab.git[push=]https://git.insanusmokrassar.com:8322/InsanusMokrassar/krontab.git"
|
||||
url = "https://git.insanusmokrassar.com:8322/InsanusMokrassar/krontab.git"
|
||||
}
|
||||
|
||||
developers {
|
||||
|
||||
developer {
|
||||
id = "InsanusMokrassar"
|
||||
name = "Ovsiannikov Aleksei"
|
||||
email = "ovsyannikov.alexey95@gmail.com"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
licenses {
|
||||
|
||||
license {
|
||||
name = "Apache Software License 2.0"
|
||||
url = "https://git.insanusmokrassar.com/InsanusMokrassar/krontab/src/master/LICENSE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "bintray"
|
||||
url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/InsanusMokrassar/${project.name}-mpp/;publish=1;override=1")
|
||||
credentials {
|
||||
username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
|
||||
password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
|
||||
}
|
||||
} else {
|
||||
it.exclude()
|
||||
}
|
||||
}
|
||||
into "${project.group}".replace(".", "/")
|
||||
|
||||
}
|
||||
pkg {
|
||||
repo = "InsanusMokrassar"
|
||||
name = "${project.name}"
|
||||
vcsUrl = "https://git.insanusmokrassar.com/InsanusMokrassar/krontab"
|
||||
licenses = ["Apache-2.0"]
|
||||
version {
|
||||
name = "${project.version}"
|
||||
released = new Date()
|
||||
vcsTag = "${project.version}"
|
||||
gpg {
|
||||
sign = true
|
||||
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.doFirst {
|
||||
publications = publishing.publications.collect {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.dependsOn publishToMavenLocal
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) {
|
||||
do {
|
||||
delay(next().unixMillisLong - DateTime.now().unixMillisLong)
|
||||
} while (block())
|
||||
}
|
||||
suspend inline fun doWhile(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> Boolean
|
||||
) = createSimpleScheduler(scheduleConfig).doWhile(block)
|
||||
|
||||
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
|
||||
block()
|
||||
true
|
||||
}
|
||||
suspend inline fun doInfinity(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> Unit
|
||||
) = createSimpleScheduler(scheduleConfig).doInfinity(block)
|
||||
|
||||
suspend inline fun KronScheduler.doOnce(noinline block: suspend () -> Unit) = doWhile {
|
||||
block()
|
||||
false
|
||||
}
|
||||
suspend inline fun doOnce(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> Unit
|
||||
) = createSimpleScheduler(scheduleConfig).doOnce(block)
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
|
||||
interface KronScheduler {
|
||||
suspend fun next(relatively: DateTime = DateTime.now()): DateTime
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.insanusmokrassar.krontab.builder.buildSchedule
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTime
|
||||
|
||||
internal val anyCronDateTime by lazy {
|
||||
CronDateTime()
|
||||
}
|
||||
val AnyTimeScheduler: KronScheduler by lazy {
|
||||
CronDateTimeScheduler(listOf(anyCronDateTime))
|
||||
}
|
||||
val EverySecondScheduler: KronScheduler
|
||||
get() = AnyTimeScheduler
|
||||
|
||||
val EveryMinuteScheduler: KronScheduler by lazy {
|
||||
buildSchedule { minutes { 0 every 1 } }
|
||||
}
|
||||
|
||||
val EveryHourScheduler: KronScheduler by lazy {
|
||||
buildSchedule { hours { 0 every 1 } }
|
||||
}
|
||||
|
||||
val EveryDayOfMonthScheduler: KronScheduler by lazy {
|
||||
buildSchedule { dayOfMonth { 0 every 1 } }
|
||||
}
|
||||
|
||||
val EveryMonthScheduler: KronScheduler by lazy {
|
||||
buildSchedule { months { 0 every 1 } }
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTime
|
||||
import com.insanusmokrassar.krontab.internal.parseDaysOfMonth
|
||||
import com.insanusmokrassar.krontab.internal.parseHours
|
||||
import com.insanusmokrassar.krontab.internal.parseMinutes
|
||||
import com.insanusmokrassar.krontab.internal.parseMonths
|
||||
import com.insanusmokrassar.krontab.internal.parseSeconds
|
||||
|
||||
/**
|
||||
* Parse [incoming] string and adapt according to next format: "* * * * *" where order of things:
|
||||
*
|
||||
* seconds
|
||||
* minutes
|
||||
* hours
|
||||
* dayOfMonth
|
||||
* month
|
||||
*
|
||||
* And each one have next format:
|
||||
*
|
||||
* {number},{number},...
|
||||
*
|
||||
* and {number} here is one of {int}-{int} OR {int}/{int} OR *\/{int} OR {int}.
|
||||
*
|
||||
* Seconds ranges can be found in [com.insanusmokrassar.krontab.internal.secondsRange].
|
||||
* Minutes ranges can be found in [com.insanusmokrassar.krontab.internal.minutesRange].
|
||||
* Hours ranges can be found in [com.insanusmokrassar.krontab.internal.hoursRange].
|
||||
* Days of month ranges can be found in [com.insanusmokrassar.krontab.internal.dayOfMonthRange].
|
||||
* Months ranges can be found in [com.insanusmokrassar.krontab.internal.monthRange].
|
||||
*
|
||||
* @sample "0/5 * * * *" for every five seconds triggering
|
||||
* @sample "0/15 30 * * *" for every 15th seconds in a half of each hour
|
||||
* @sample "1 2 3 4 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||
*/
|
||||
fun createSimpleScheduler(incoming: String): KronScheduler {
|
||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ")
|
||||
|
||||
val secondsParsed = parseSeconds(secondsSource)
|
||||
val minutesParsed = parseMinutes(minutesSource)
|
||||
val hoursParsed = parseHours(hoursSource)
|
||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||
val monthParsed = parseMonths(monthSource)
|
||||
|
||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||
|
||||
secondsParsed ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(seconds = currentTime)
|
||||
}
|
||||
|
||||
minutesParsed ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(minutes = currentTime)
|
||||
}
|
||||
|
||||
hoursParsed ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(hours = currentTime)
|
||||
}
|
||||
|
||||
dayOfMonthParsed ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(dayOfMonth = currentTime)
|
||||
}
|
||||
|
||||
monthParsed ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(month = currentTime)
|
||||
}
|
||||
|
||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.insanusmokrassar.krontab.builder
|
||||
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
|
||||
sealed class TimeBuilder (
|
||||
private val restrictionsRange: IntRange
|
||||
) {
|
||||
private var result: Set<Int>? = null
|
||||
|
||||
fun allowAll() {
|
||||
result = null
|
||||
}
|
||||
|
||||
infix fun include(array: Array<Int>) {
|
||||
val clamped = array.map { it.clamp(restrictionsRange) } + (result ?: emptySet())
|
||||
result = clamped.toSet()
|
||||
}
|
||||
|
||||
infix fun at(value: Int) {
|
||||
result = (result ?: emptySet()) + value.clamp(restrictionsRange)
|
||||
}
|
||||
|
||||
inline fun from(value: Int) = value
|
||||
|
||||
infix fun Int.every(delay: Int): Array<Int> {
|
||||
val progression = clamp(restrictionsRange) .. restrictionsRange.last step delay
|
||||
val result = progression.toSet().toTypedArray()
|
||||
|
||||
this@TimeBuilder include result
|
||||
|
||||
return result
|
||||
}
|
||||
infix fun every(delay: Int): Array<Int> = 0 every delay
|
||||
|
||||
infix fun Int.upTo(endIncluding: Int): Array<Int> {
|
||||
val progression = clamp(restrictionsRange) .. endIncluding.clamp(restrictionsRange)
|
||||
val result = progression.toSet().toTypedArray()
|
||||
|
||||
this@TimeBuilder include result
|
||||
|
||||
return result
|
||||
}
|
||||
infix fun upTo(endIncluding: Int): Array<Int> = 0 upTo endIncluding
|
||||
|
||||
internal fun build() = result ?.map { it.toByte() } ?.toTypedArray()
|
||||
}
|
||||
|
||||
class SecondsBuilder : TimeBuilder(secondsRange)
|
||||
class MinutesBuilder : TimeBuilder(minutesRange)
|
||||
class HoursBuilder : TimeBuilder(com.insanusmokrassar.krontab.internal.hoursRange)
|
||||
class DaysOfMonthBuilder : TimeBuilder(com.insanusmokrassar.krontab.internal.dayOfMonthRange)
|
||||
class MonthsBuilder : TimeBuilder(monthRange)
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
import com.soywiz.klock.DateTime
|
||||
import com.soywiz.klock.DateTimeSpan
|
||||
|
||||
/**
|
||||
* [month] 0-11
|
||||
* [dayOfMonth] 0-31
|
||||
* [hours] 0-23
|
||||
* [minutes] 0-59
|
||||
* [seconds] 0-59
|
||||
*/
|
||||
internal data class CronDateTime(
|
||||
val month: Byte? = null,
|
||||
val dayOfMonth: Byte? = null,
|
||||
val hours: Byte? = null,
|
||||
val minutes: Byte? = null,
|
||||
val seconds: Byte? = null
|
||||
) {
|
||||
init {
|
||||
check(month ?.let { it in com.insanusmokrassar.krontab.internal.monthRange } ?: true)
|
||||
check(dayOfMonth ?.let { it in com.insanusmokrassar.krontab.internal.dayOfMonthRange } ?: true)
|
||||
check(hours?.let { it in com.insanusmokrassar.krontab.internal.hoursRange } ?: true)
|
||||
check(minutes?.let { it in com.insanusmokrassar.krontab.internal.minutesRange } ?: true)
|
||||
check(seconds?.let { it in com.insanusmokrassar.krontab.internal.secondsRange } ?: true)
|
||||
}
|
||||
|
||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
month: Int? = null,
|
||||
dayOfMonth: Int? = null,
|
||||
hours: Int? = null,
|
||||
minutes: Int? = null,
|
||||
seconds: Int? = null
|
||||
) = CronDateTime(
|
||||
month ?.clamp(com.insanusmokrassar.krontab.internal.monthRange) ?.toByte(),
|
||||
dayOfMonth ?.clamp(com.insanusmokrassar.krontab.internal.dayOfMonthRange) ?.toByte(),
|
||||
hours ?.clamp(com.insanusmokrassar.krontab.internal.hoursRange) ?.toByte(),
|
||||
minutes ?.clamp(com.insanusmokrassar.krontab.internal.minutesRange) ?.toByte(),
|
||||
seconds ?.clamp(com.insanusmokrassar.krontab.internal.secondsRange) ?.toByte()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime {
|
||||
var current = relativelyTo
|
||||
|
||||
seconds?.let {
|
||||
val left = it - current.seconds
|
||||
current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left)
|
||||
}
|
||||
|
||||
minutes?.let {
|
||||
val left = it - current.minutes
|
||||
current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left)
|
||||
}
|
||||
|
||||
hours?.let {
|
||||
val left = it - current.hours
|
||||
current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left)
|
||||
}
|
||||
|
||||
klockDayOfMonth ?.let {
|
||||
val left = it - current.dayOfMonth
|
||||
current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
||||
}
|
||||
|
||||
month ?.let {
|
||||
val left = it - current.month0
|
||||
current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import com.soywiz.klock.DateTime
|
||||
|
||||
internal data class CronDateTimeScheduler internal constructor(
|
||||
internal val cronDateTimes: List<CronDateTime>
|
||||
) : KronScheduler {
|
||||
override suspend fun next(relatively: DateTime): DateTime {
|
||||
return cronDateTimes.map { it.toNearDateTime(relatively) }.min() ?: anyCronDateTime.toNearDateTime(relatively)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
|
||||
internal fun Int.clamp(min: Int, max: Int): Int = if (this < min) min else if (this > max) max else this
|
||||
internal fun Int.clamp(range: IntRange): Int = clamp(range.first, range.last)
|
||||
57
src/commonMain/kotlin/dev/inmo/krontab/Executes.kt
Normal file
57
src/commonMain/kotlin/dev/inmo/krontab/Executes.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
|
||||
*
|
||||
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
|
||||
*/
|
||||
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
|
||||
delay((next() - DateTime.now()).millisecondsLong)
|
||||
return block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it
|
||||
* @see buildSchedule
|
||||
*/
|
||||
suspend inline fun <T> doOnce(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> T
|
||||
) = buildSchedule(scheduleConfig).doOnce(block)
|
||||
|
||||
/**
|
||||
* Will execute [block] while it will return true as a result of its calculation
|
||||
*/
|
||||
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) {
|
||||
do { val doNext = doOnce(block) } while (doNext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
|
||||
*
|
||||
* @see buildSchedule
|
||||
*/
|
||||
suspend inline fun doWhile(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> Boolean
|
||||
) = buildSchedule(scheduleConfig).doWhile(block)
|
||||
|
||||
/**
|
||||
* Will execute [block] without any checking of result
|
||||
*/
|
||||
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
|
||||
block()
|
||||
true
|
||||
}
|
||||
/**
|
||||
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
|
||||
*
|
||||
* @see buildSchedule
|
||||
*/
|
||||
suspend inline fun doInfinity(
|
||||
scheduleConfig: String,
|
||||
noinline block: suspend () -> Unit
|
||||
) = buildSchedule(scheduleConfig).doInfinity(block)
|
||||
21
src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt
Normal file
21
src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
|
||||
/**
|
||||
* This interface was created for abstraction of [next] operation. Currently, there is only
|
||||
* [dev.inmo.krontab.internal.CronDateTimeScheduler] realisation of this interface inside of this library,
|
||||
* but you it is possible to create your own realisation of this interface for scheduling, for example, depending of
|
||||
* users activity or something like this
|
||||
*
|
||||
* @see dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
*/
|
||||
interface KronScheduler {
|
||||
|
||||
/**
|
||||
* @return Next [DateTime] when some action must be triggered according to settings of this instance
|
||||
*
|
||||
* @see dev.inmo.krontab.internal.CronDateTimeScheduler.next
|
||||
*/
|
||||
suspend fun next(relatively: DateTime = DateTime.now()): DateTime
|
||||
}
|
||||
51
src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt
Normal file
51
src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import dev.inmo.krontab.builder.buildSchedule
|
||||
import dev.inmo.krontab.internal.CronDateTime
|
||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
|
||||
internal val anyCronDateTime by lazy {
|
||||
CronDateTime()
|
||||
}
|
||||
|
||||
/**
|
||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||
*/
|
||||
val AnyTimeScheduler: KronScheduler by lazy {
|
||||
CronDateTimeScheduler(listOf(anyCronDateTime))
|
||||
}
|
||||
|
||||
/**
|
||||
* [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 } }
|
||||
}
|
||||
72
src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt
Normal file
72
src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt
Normal file
@@ -0,0 +1,72 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import dev.inmo.krontab.internal.*
|
||||
|
||||
/**
|
||||
* @see createSimpleScheduler
|
||||
* @see buildSchedule
|
||||
*/
|
||||
typealias KrontabTemplate = String
|
||||
|
||||
/**
|
||||
* Parse [incoming] string and adapt according to next format: "* * * * *" where order of things:
|
||||
*
|
||||
* * seconds
|
||||
* * minutes
|
||||
* * hours
|
||||
* * dayOfMonth
|
||||
* * month
|
||||
*
|
||||
* And each one have next format:
|
||||
*
|
||||
* `{number}[,{number},...]` or `*`
|
||||
*
|
||||
* and {number} here is one of
|
||||
*
|
||||
* * {int}-{int}
|
||||
* * {int}/{int}
|
||||
* * */{int}
|
||||
* * {int}
|
||||
* * F
|
||||
* * L
|
||||
*
|
||||
* 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]
|
||||
*
|
||||
* 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
|
||||
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||
*
|
||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||
*/
|
||||
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ")
|
||||
|
||||
val secondsParsed = parseSeconds(secondsSource)
|
||||
val minutesParsed = parseMinutes(minutesSource)
|
||||
val hoursParsed = parseHours(hoursSource)
|
||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||
val monthParsed = parseMonths(monthSource)
|
||||
|
||||
return createKronScheduler(
|
||||
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for [createSimpleScheduler]
|
||||
*/
|
||||
fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleScheduler(incoming)
|
||||
|
||||
/**
|
||||
* Shortcut for [buildSchedule]
|
||||
*/
|
||||
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.insanusmokrassar.krontab.builder
|
||||
package dev.inmo.krontab.builder
|
||||
|
||||
import com.insanusmokrassar.krontab.CronDateTimeScheduler
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTime
|
||||
import com.insanusmokrassar.krontab.internal.fillWith
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
import dev.inmo.krontab.internal.createKronScheduler
|
||||
|
||||
/**
|
||||
* Will help to create an instance of [KronScheduler]
|
||||
*
|
||||
* @see dev.inmo.krontab.createSimpleScheduler
|
||||
*/
|
||||
fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
|
||||
val builder = SchedulerBuilder()
|
||||
|
||||
@@ -36,6 +39,9 @@ class SchedulerBuilder(
|
||||
} ?: builderValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an seconds block
|
||||
*/
|
||||
fun seconds(block: SecondsBuilder.() -> Unit) {
|
||||
seconds = callAndReturn(
|
||||
seconds,
|
||||
@@ -44,6 +50,9 @@ class SchedulerBuilder(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an minutes block
|
||||
*/
|
||||
fun minutes(block: MinutesBuilder.() -> Unit) {
|
||||
minutes = callAndReturn(
|
||||
minutes,
|
||||
@@ -52,6 +61,9 @@ class SchedulerBuilder(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an hours block
|
||||
*/
|
||||
fun hours(block: HoursBuilder.() -> Unit) {
|
||||
hours = callAndReturn(
|
||||
hours,
|
||||
@@ -60,6 +72,9 @@ class SchedulerBuilder(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an days of month block
|
||||
*/
|
||||
fun dayOfMonth(block: DaysOfMonthBuilder.() -> Unit) {
|
||||
dayOfMonth = callAndReturn(
|
||||
dayOfMonth,
|
||||
@@ -68,6 +83,9 @@ class SchedulerBuilder(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an months block
|
||||
*/
|
||||
fun months(block: MonthsBuilder.() -> Unit) {
|
||||
month = callAndReturn(
|
||||
month,
|
||||
@@ -76,29 +94,11 @@ class SchedulerBuilder(
|
||||
)
|
||||
}
|
||||
|
||||
fun build(): KronScheduler {
|
||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||
|
||||
seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(seconds = currentTime)
|
||||
}
|
||||
|
||||
minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(minutes = currentTime)
|
||||
}
|
||||
|
||||
hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(hours = currentTime)
|
||||
}
|
||||
|
||||
dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(dayOfMonth = currentTime)
|
||||
}
|
||||
|
||||
month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(month = currentTime)
|
||||
}
|
||||
|
||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||
}
|
||||
/**
|
||||
* @return Completely built and independent [KronScheduler]
|
||||
*
|
||||
* @see dev.inmo.krontab.createSimpleScheduler
|
||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||
*/
|
||||
fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month)
|
||||
}
|
||||
129
src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt
Normal file
129
src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt
Normal file
@@ -0,0 +1,129 @@
|
||||
package dev.inmo.krontab.builder
|
||||
|
||||
import dev.inmo.krontab.internal.*
|
||||
import dev.inmo.krontab.utils.clamp
|
||||
|
||||
/**
|
||||
* This class was created for incapsulation of builder work with specified [restrictionsRange]. For example,
|
||||
* [include] function of [TimeBuilder] will always [clamp] incoming data using its [restrictionsRange]
|
||||
*/
|
||||
sealed class TimeBuilder (
|
||||
private val restrictionsRange: IntRange
|
||||
) {
|
||||
private var result: Set<Int>? = null
|
||||
|
||||
/**
|
||||
* The first possible value of builder
|
||||
*/
|
||||
val first
|
||||
get() = restrictionsRange.first
|
||||
/**
|
||||
* The last possible value of builder. Using of this variable equal to using "L" in strings
|
||||
*/
|
||||
val last
|
||||
get() = restrictionsRange.last
|
||||
|
||||
/**
|
||||
* After calling of this function this builder will allow any value of current time
|
||||
*/
|
||||
@Suppress("unused")
|
||||
fun allowAll() {
|
||||
result = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Will include all variations from this array inside of this timeline
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
infix fun include(array: Array<Int>) {
|
||||
val clamped = array.map { it.clamp(restrictionsRange) } + (result ?: emptySet())
|
||||
result = clamped.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one [value] to current timeline
|
||||
*/
|
||||
@Suppress("unused")
|
||||
infix fun at(value: Int) {
|
||||
result = (result ?: emptySet()) + value.clamp(restrictionsRange)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shortcut for [at]. In fact will
|
||||
*/
|
||||
@Suppress("unused", "NOTHING_TO_INLINE")
|
||||
inline infix fun each(value: Int) = at(value)
|
||||
|
||||
/**
|
||||
* Just wrapper for more obvious writing something like "[from] 2 [every] 5". For example, for [SecondsBuilder] it
|
||||
* will mean "[from] second second [every] 5 seconds", or "2, 7, 13, ..."
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline infix fun from(value: Int) = value
|
||||
|
||||
/**
|
||||
* Will create an sequence of times starting [from] [this] [every] [delay] times. For example, for [SecondsBuilder] it
|
||||
* will mean "[from] second second [every] 5 seconds", or "2, 7, 13, ..."
|
||||
*
|
||||
* @see [from]
|
||||
*/
|
||||
infix fun Int.every(delay: Int): Array<Int> {
|
||||
val progression = clamp(restrictionsRange) .. restrictionsRange.last step delay
|
||||
val result = progression.toSet().toTypedArray()
|
||||
|
||||
this@TimeBuilder include result
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for "[from] 0 [every] [delay]"
|
||||
*/
|
||||
infix fun every(delay: Int): Array<Int> = this from 0 every delay
|
||||
|
||||
/**
|
||||
* Will fill up this timeline from [this] up to [endIncluding]
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
infix fun Int.upTo(endIncluding: Int): Array<Int> {
|
||||
val progression = clamp(restrictionsRange) .. endIncluding.clamp(restrictionsRange)
|
||||
val result = progression.toSet().toTypedArray()
|
||||
|
||||
this@TimeBuilder include result
|
||||
|
||||
return result
|
||||
}
|
||||
/**
|
||||
* Shortcut for "[from] 0 [upTo] [endIncluding]"
|
||||
*/
|
||||
@Suppress("unused")
|
||||
infix fun upTo(endIncluding: Int): Array<Int> = this from 0 upTo endIncluding
|
||||
/**
|
||||
* Will fill up this timeline from [this] up to [endIncluding]
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
infix operator fun Int.rangeTo(endIncluding: Int) = upTo(endIncluding)
|
||||
/**
|
||||
* Shortcut for "[from] 0 [rangeTo] [endIncluding]"
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
infix operator fun rangeTo(endIncluding: Int) = (this from 0) rangeTo endIncluding
|
||||
|
||||
/**
|
||||
* Will include the last possible value
|
||||
*/
|
||||
fun includeLast() = at(restrictionsRange.last)
|
||||
/**
|
||||
* Will include the first possible value
|
||||
*/
|
||||
fun includeFirst() = at(restrictionsRange.first)
|
||||
|
||||
internal fun build() = result ?.map { it.toByte() } ?.toTypedArray()
|
||||
}
|
||||
|
||||
class SecondsBuilder : TimeBuilder(secondsRange)
|
||||
class MinutesBuilder : TimeBuilder(minutesRange)
|
||||
class HoursBuilder : TimeBuilder(hoursRange)
|
||||
class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange)
|
||||
class MonthsBuilder : TimeBuilder(monthRange)
|
||||
@@ -0,0 +1,25 @@
|
||||
package dev.inmo.krontab.collection
|
||||
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun CollectionKronScheduler.includeAll(kronSchedulers: List<KronScheduler>) {
|
||||
kronSchedulers.forEach {
|
||||
include(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun CollectionKronScheduler.includeAll(vararg kronSchedulers: KronScheduler) {
|
||||
includeAll(kronSchedulers.toList())
|
||||
}
|
||||
|
||||
operator fun KronScheduler.plus(kronScheduler: KronScheduler): CollectionKronScheduler {
|
||||
return CollectionKronScheduler().apply {
|
||||
includeAll(this, kronScheduler)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun CollectionKronScheduler.plusAssign(kronScheduler: KronScheduler) {
|
||||
include(kronScheduler)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dev.inmo.krontab.collection
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
import dev.inmo.krontab.anyCronDateTime
|
||||
import dev.inmo.krontab.internal.*
|
||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
import dev.inmo.krontab.internal.merge
|
||||
import dev.inmo.krontab.internal.toNearDateTime
|
||||
|
||||
/**
|
||||
* This scheduler will be useful in case you want to unite several different [KronScheduler]s
|
||||
*/
|
||||
data class CollectionKronScheduler internal constructor(
|
||||
internal val schedulers: MutableList<KronScheduler>
|
||||
) : KronScheduler {
|
||||
internal constructor() : this(mutableListOf())
|
||||
|
||||
/**
|
||||
* Add [kronScheduler] into its [schedulers] list
|
||||
*
|
||||
* * When [kronScheduler] is [CronDateTimeScheduler] it will merge all [CronDateTimeScheduler]s from [schedulers] list
|
||||
* and this [kronScheduler] using [merge] function
|
||||
* * When [kronScheduler] is [CollectionKronScheduler] it this instance will include all [kronScheduler]
|
||||
* [schedulers]
|
||||
* * Otherwise [kronScheduler] will be added to [schedulers] list
|
||||
*/
|
||||
fun include(kronScheduler: KronScheduler) {
|
||||
when (kronScheduler) {
|
||||
is CronDateTimeScheduler -> {
|
||||
val resultCronDateTimes = mutableListOf(kronScheduler)
|
||||
schedulers.removeAll {
|
||||
if (it is CronDateTimeScheduler) {
|
||||
resultCronDateTimes.add(it)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
schedulers.add(
|
||||
merge(resultCronDateTimes)
|
||||
)
|
||||
}
|
||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||
include(it)
|
||||
}
|
||||
else -> schedulers.add(kronScheduler)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun next(relatively: DateTime): DateTime {
|
||||
return schedulers.minOfOrNull { it.next(relatively) } ?: anyCronDateTime.toNearDateTime(relatively)
|
||||
}
|
||||
}
|
||||
105
src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt
Normal file
105
src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import com.soywiz.klock.DateTimeSpan
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
|
||||
/**
|
||||
* @param month 0-11
|
||||
* @param dayOfMonth 0-31
|
||||
* @param hours 0-23
|
||||
* @param minutes 0-59
|
||||
* @param seconds 0-59
|
||||
*/
|
||||
internal data class CronDateTime(
|
||||
val month: Byte? = null,
|
||||
val dayOfMonth: Byte? = null,
|
||||
val hours: Byte? = null,
|
||||
val minutes: Byte? = null,
|
||||
val seconds: Byte? = null
|
||||
) {
|
||||
init {
|
||||
check(month ?.let { it in monthRange } ?: true)
|
||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
||||
check(hours?.let { it in hoursRange } ?: true)
|
||||
check(minutes?.let { it in minutesRange } ?: true)
|
||||
check(seconds?.let { it in secondsRange } ?: true)
|
||||
}
|
||||
|
||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
||||
*/
|
||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime {
|
||||
var current = relativelyTo
|
||||
|
||||
seconds?.let {
|
||||
val left = it - current.seconds
|
||||
current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left)
|
||||
}
|
||||
|
||||
minutes?.let {
|
||||
val left = it - current.minutes
|
||||
current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left)
|
||||
}
|
||||
|
||||
hours?.let {
|
||||
val left = it - current.hours
|
||||
current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left)
|
||||
}
|
||||
|
||||
klockDayOfMonth ?.let {
|
||||
val left = (it - current.dayOfMonth).let { diff ->
|
||||
if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) {
|
||||
0
|
||||
} else {
|
||||
diff
|
||||
}
|
||||
}
|
||||
current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
||||
}
|
||||
|
||||
month ?.let {
|
||||
val left = it - current.month0
|
||||
current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
||||
*/
|
||||
internal fun createKronScheduler(
|
||||
seconds: Array<Byte>? = null,
|
||||
minutes: Array<Byte>? = null,
|
||||
hours: Array<Byte>? = null,
|
||||
dayOfMonth: Array<Byte>? = null,
|
||||
month: Array<Byte>? = null
|
||||
): KronScheduler {
|
||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||
|
||||
seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(seconds = currentTime)
|
||||
}
|
||||
|
||||
minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(minutes = currentTime)
|
||||
}
|
||||
|
||||
hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(hours = currentTime)
|
||||
}
|
||||
|
||||
dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(dayOfMonth = currentTime)
|
||||
}
|
||||
|
||||
month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||
previousCronDateTime.copy(month = currentTime)
|
||||
}
|
||||
|
||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
import dev.inmo.krontab.anyCronDateTime
|
||||
|
||||
/**
|
||||
* Cron-oriented realisation of [KronScheduler]
|
||||
*
|
||||
* @see dev.inmo.krontab.AnyTimeScheduler
|
||||
* @see dev.inmo.krontab.EverySecondScheduler
|
||||
* @see dev.inmo.krontab.EveryMinuteScheduler
|
||||
* @see dev.inmo.krontab.EveryHourScheduler
|
||||
* @see dev.inmo.krontab.EveryDayOfMonthScheduler
|
||||
* @see dev.inmo.krontab.EveryMonthScheduler
|
||||
*
|
||||
* @see dev.inmo.krontab.builder.buildSchedule
|
||||
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
||||
*/
|
||||
@Deprecated("This class will get internal status in future")
|
||||
data class CronDateTimeScheduler internal constructor(
|
||||
internal val cronDateTimes: List<CronDateTime>
|
||||
) : KronScheduler {
|
||||
/**
|
||||
* @return Near date using [cronDateTimes] list and getting the [Iterable.min] one
|
||||
*
|
||||
* @see toNearDateTime
|
||||
*/
|
||||
override suspend fun next(relatively: DateTime): DateTime {
|
||||
return cronDateTimes.map { it.toNearDateTime(relatively) }.minOrNull() ?: anyCronDateTime.toNearDateTime(relatively)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of
|
||||
* [kronDateTimeSchedulers] included
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
fun merge(kronDateTimeSchedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
||||
kronDateTimeSchedulers.flatMap { it.cronDateTimes }.distinct()
|
||||
)
|
||||
|
||||
/**
|
||||
* @return Vararg shortcyut for [merge]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun merge(vararg kronDateTimeSchedulers: CronDateTimeScheduler) = merge(kronDateTimeSchedulers.toList())
|
||||
/**
|
||||
* Use [merge] operation to internalcreate new [CronDateTimeScheduler] with all [CronDateTimeScheduler.cronDateTimes]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun CronDateTimeScheduler.plus(other: CronDateTimeScheduler) = merge(this, other)
|
||||
@@ -1,14 +1,23 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
import dev.inmo.krontab.utils.clamp
|
||||
|
||||
private fun createSimpleScheduler(from: String, dataRange: IntRange): Array<Byte>? {
|
||||
val things = from.split(",")
|
||||
|
||||
val results = things.flatMap {
|
||||
val currentToken = it.toLowerCase().replace(
|
||||
"f", dataRange.first.toString()
|
||||
).replace(
|
||||
"l", dataRange.last.toString()
|
||||
)
|
||||
when {
|
||||
it.contains("/") -> {
|
||||
val (start, step) = it.split("/")
|
||||
currentToken.contains("-") -> {
|
||||
val splitted = currentToken.split("-")
|
||||
(splitted.first().toInt().clamp(dataRange) .. splitted[1].toInt().clamp(dataRange)).toList()
|
||||
}
|
||||
currentToken.contains("/") -> {
|
||||
val (start, step) = currentToken.split("/")
|
||||
val startNum = (if (start.isEmpty() || start == "*") {
|
||||
0
|
||||
} else {
|
||||
@@ -17,8 +26,8 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange): Array<Byte
|
||||
val stepNum = step.toInt().clamp(dataRange)
|
||||
(startNum .. dataRange.last step stepNum).map { it }
|
||||
}
|
||||
it == "*" -> return null
|
||||
else -> listOf(it.toInt().clamp(dataRange))
|
||||
currentToken == "*" -> return null
|
||||
else -> listOf(currentToken.toInt().clamp(dataRange))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
internal val monthRange = 0 .. 11
|
||||
internal val dayOfMonthRange = 0 .. 30
|
||||
12
src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt
Normal file
12
src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
/**
|
||||
* @return [min] in case if [this] less than [min]. Otherwise will check that [max] grant than [this] and return [this]
|
||||
* if so or [max] otherwise
|
||||
*/
|
||||
internal fun Int.clamp(min: Int, max: Int): Int = if (this < min) min else if (this > max) max else this
|
||||
|
||||
/**
|
||||
* Wrapper function for [clamp] extension
|
||||
*/
|
||||
internal fun Int.clamp(range: IntRange): Int = clamp(range.first, range.last)
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.soywiz.klock.DateTime
|
||||
import kotlinx.coroutines.*
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@FlowPreview
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import com.insanusmokrassar.krontab.builder.buildSchedule
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.builder.buildSchedule
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
@@ -0,0 +1,79 @@
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import dev.inmo.krontab.buildSchedule
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@FlowPreview
|
||||
class StringParseTest {
|
||||
@Test
|
||||
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
|
||||
val kronScheduler = buildSchedule("*/1 * * * *")
|
||||
|
||||
val flow = kronScheduler.asFlow()
|
||||
|
||||
runTest {
|
||||
val mustBeCollected = 10
|
||||
var collected = 0
|
||||
flow.takeWhile {
|
||||
collected < mustBeCollected
|
||||
}.collect {
|
||||
collected++
|
||||
}
|
||||
assertEquals(mustBeCollected, collected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
|
||||
val kronScheduler = buildSchedule("*/1 * * * *")
|
||||
|
||||
val flow = kronScheduler.asFlow()
|
||||
|
||||
runTest {
|
||||
val testsCount = 10
|
||||
val failJob = it.createFailJob((testsCount * 2) * 1000L)
|
||||
val mustBeCollected = 10
|
||||
val answers = (0 until testsCount).map { _ ->
|
||||
it.async {
|
||||
var collected = 0
|
||||
flow.takeWhile {
|
||||
collected < mustBeCollected
|
||||
}.collect {
|
||||
collected++
|
||||
}
|
||||
collected
|
||||
}
|
||||
}.awaitAll()
|
||||
|
||||
failJob.cancel()
|
||||
|
||||
answers.forEach {
|
||||
assertEquals(mustBeCollected, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun testThatFlowIsCorrectlyWorkEverySeveralSecondsRangeBuiltOnString() {
|
||||
val rangesEnds = listOf(0 to 5, 30 to 35)
|
||||
val kronScheduler = buildSchedule("${rangesEnds.joinToString(",") { "${it.first}-${it.second}" }} * * * *")
|
||||
|
||||
val flow = kronScheduler.asFlow()
|
||||
|
||||
runTest {
|
||||
val ranges = rangesEnds.map { it.first .. it.second }.flatten().toMutableList()
|
||||
val expectedCollects = rangesEnds.sumBy { it.second - it.first + 1 }
|
||||
var collected = 0
|
||||
|
||||
flow.takeWhile { ranges.isNotEmpty() }.collect {
|
||||
ranges.remove(it.seconds)
|
||||
collected++
|
||||
}
|
||||
assertEquals(expectedCollects, collected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
Reference in New Issue
Block a user