mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2025-12-05 04:25:48 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a0870144b | |||
| 40adf80d6f | |||
| 0d1e382182 | |||
| a15b4162fb | |||
| d52435a014 | |||
| 9ce69efad5 | |||
| 092fc6bc9b | |||
| ec85fd77f9 | |||
| 496d4cf100 | |||
| dce0fd87a1 | |||
| fd842b17fa | |||
| 726824fb4f | |||
| e04d14ccb1 | |||
| 3f132da62d | |||
| 3cce44664f | |||
| 0376eb7910 | |||
| bbb4f41723 | |||
| 365adb1016 | |||
| 476239faa6 | |||
| 0a2b5ef6a0 | |||
| aedb49f6f8 | |||
| 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 |
22
.github/workflows/publishing_packages.yml
vendored
Normal file
22
.github/workflows/publishing_packages.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
name: Publish package to GitHub Packages
|
||||
on: [push]
|
||||
jobs:
|
||||
publishing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- 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 package
|
||||
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signMetadataPublication -x signAndroidDebugPublication -x signAndroidReleasePublication
|
||||
env:
|
||||
GITHUBPACKAGES_USER: ${{ github.actor }}
|
||||
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
||||
96
CHANGELOG.md
96
CHANGELOG.md
@@ -1,5 +1,81 @@
|
||||
# Changelog
|
||||
|
||||
## 0.5.1
|
||||
|
||||
* Versions
|
||||
* `Kotlin`: `1.4.21` -> `1.4.31`
|
||||
* `Coroutines`: `1.4.2` -> `1.4.3`
|
||||
* `Klock`: `2.0.3` -> `2.0.7`
|
||||
* `Androidx Work`: `2.4.0` -> `2.5.0`
|
||||
|
||||
## 0.5.0 Years
|
||||
|
||||
**BREAKING CHANGES**
|
||||
|
||||
* `CronDateTimeScheduler` has been marked as `internal` and no longer accessible outside of internal functions
|
||||
* Old methods `merge` and `plus` related to `CronDateTimeScheduler` has been marked as `deprecated` and changed their
|
||||
parameters types - it is `KronScheduler` now
|
||||
* New methods `merge` has been added
|
||||
* **`KronScheduler#next` method now is nullable. Use `nextOrRelative`/`nextOrNow` to get next time certainly**
|
||||
* **Years was added as optional part of krontab template and opportunity in `SchedulerBuilder`**
|
||||
* New builder `YearsBuilder`
|
||||
* `SchedulerFlow#collectSafely` will be normally (without exceptions) finish when `next` of scheduler will return
|
||||
null
|
||||
* `KronScheduler#doOnce` will run code immediately in case when `next` is returning null value
|
||||
* `KrontabTemplateWrapper` has been added
|
||||
* New extension `KrontabTemplate#toKronScheduler` (works as `toSchedule`)
|
||||
* **Fixed issue related to the fact that `toNearDateTime` of `CronDateTime` incorrectly handled months**
|
||||
* **Android target has been added**
|
||||
|
||||
## 0.4.2
|
||||
|
||||
* 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:
|
||||
@@ -9,13 +85,6 @@
|
||||
* Typealias `KrontabTemplate` was added
|
||||
* Extension `KrontabTemplate#toSchedule` was added
|
||||
|
||||
## 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.4
|
||||
|
||||
* Updates in libraries:
|
||||
@@ -23,7 +92,7 @@
|
||||
* 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
|
||||
## 0.2.3
|
||||
|
||||
* Updates in libraries:
|
||||
* Kotlin `1.3.70` -> `1.3.72`
|
||||
@@ -35,13 +104,20 @@
|
||||
* `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
|
||||
## 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
|
||||
## 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`
|
||||
|
||||
30
README.md
30
README.md
@@ -1,7 +1,7 @@
|
||||
# krontab
|
||||
|
||||
[  ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab/_latestVersion)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.insanusmokrassar/krontab)
|
||||
[ ](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
|
||||
@@ -39,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`.
|
||||
|
||||
@@ -53,18 +53,18 @@ For old version of Gradle, instead of `implementation` word developers must use
|
||||
Developers can use more simple way to configure repeat times is string. String configuring
|
||||
like a `crontab`, but with a little bit different meanings:
|
||||
```
|
||||
/-------- Seconds
|
||||
| /------ Minutes
|
||||
| | /---- Hours
|
||||
| | | /-- Days of months
|
||||
| | | | / Months
|
||||
| | | | |
|
||||
* * * * *
|
||||
/---------- Seconds
|
||||
| /-------- Minutes
|
||||
| | /------ Hours
|
||||
| | | /---- Days of months
|
||||
| | | | /-- Months
|
||||
| | | | | / (optional) Year
|
||||
* * * * * *
|
||||
```
|
||||
|
||||
It is different with original `crontab` syntax for the reason, that expected that in practice developers
|
||||
will use seconds and minutes with more probability than months (for example). In fact, developers will use something
|
||||
like:
|
||||
will use seconds and minutes with more probability than months (for example) or even years. In fact, developers will use
|
||||
something like:
|
||||
|
||||
```kotlin
|
||||
doWhile("/5 * * * *") {
|
||||
@@ -73,7 +73,7 @@ doWhile("/5 * * * *") {
|
||||
}
|
||||
```
|
||||
|
||||
Or more version:
|
||||
An other version:
|
||||
|
||||
```kotlin
|
||||
doInfinity("/5 * * * *") {
|
||||
@@ -85,7 +85,7 @@ Both of examples will print `Called` message every five seconds.
|
||||
|
||||
### Config via builder
|
||||
|
||||
Also this library currently supports DSL for creating the same goals:
|
||||
Also, this library currently supports DSL for creating the same goals:
|
||||
|
||||
```kotlin
|
||||
val kronScheduler = buildSchedule {
|
||||
|
||||
71
build.gradle
71
build.gradle
@@ -3,11 +3,14 @@ buildscript {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
|
||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
||||
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +18,20 @@ plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
|
||||
id "org.jetbrains.dokka" version "$dokka_version"
|
||||
}
|
||||
apply plugin: "com.android.library"
|
||||
|
||||
project.version = "0.3.0"
|
||||
project.group = "com.insanusmokrassar"
|
||||
project.version = "$version"
|
||||
project.group = "dev.inmo"
|
||||
|
||||
apply from: "publish.gradle"
|
||||
apply from: "github_release.gradle"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||
google()
|
||||
}
|
||||
|
||||
apply from: './dokka.gradle'
|
||||
@@ -36,6 +42,9 @@ kotlin {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
android {
|
||||
publishAllLibraryVariants()
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
@@ -47,25 +56,71 @@ kotlin {
|
||||
api "com.soywiz.korlibs.klock:klock:$klockVersion"
|
||||
}
|
||||
}
|
||||
|
||||
androidMain {
|
||||
dependencies {
|
||||
api "androidx.work:work-runtime-ktx:$androidx_work_version"
|
||||
}
|
||||
}
|
||||
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-common')
|
||||
implementation kotlin('test-annotations-common')
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
}
|
||||
}
|
||||
androidTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.getkeepsafe.dexcount'
|
||||
|
||||
android {
|
||||
compileSdkVersion "$android_compileSdkVersion".toInteger()
|
||||
buildToolsVersion "$android_buildToolsVersion"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion "$android_minSdkVersion".toInteger()
|
||||
targetSdkVersion "$android_compileSdkVersion".toInteger()
|
||||
versionCode "${android_code_version}".toInteger()
|
||||
versionName "$version"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
debug {
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
test {
|
||||
java.srcDir file("src/jvmTest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
55
dokka.gradle
55
dokka.gradle
@@ -1,42 +1,45 @@
|
||||
dokka {
|
||||
outputFormat = 'html'
|
||||
|
||||
dokkaHtml {
|
||||
String dokkaOutputDirectoryPath = null
|
||||
switch (true) {
|
||||
case project.hasProperty("DOKKA_PATH"):
|
||||
outputDirectory = project.property("DOKKA_PATH").toString()
|
||||
dokkaOutputDirectoryPath = project.property("DOKKA_PATH").toString()
|
||||
break
|
||||
case System.getenv("DOKKA_PATH") != null:
|
||||
outputDirectory = System.getenv("DOKKA_PATH")
|
||||
dokkaOutputDirectoryPath = System.getenv("DOKKA_PATH")
|
||||
break
|
||||
}
|
||||
if (dokkaOutputDirectoryPath != null) {
|
||||
File outputDirectoryFile = new File(dokkaOutputDirectoryPath)
|
||||
outputDirectoryFile.mkdirs()
|
||||
outputDirectory = outputDirectoryFile
|
||||
}
|
||||
|
||||
multiplatform {
|
||||
global {
|
||||
perPackageOption {
|
||||
prefix = "com.insanusmokrassar"
|
||||
skipDeprecated = true
|
||||
includeNonPublic = true
|
||||
reportUndocumented = true
|
||||
}
|
||||
moduleName.set("kdocs")
|
||||
|
||||
dokkaSourceSets {
|
||||
configureEach {
|
||||
skipDeprecated.set(true)
|
||||
includeNonPublic.set(true)
|
||||
reportUndocumented.set(true)
|
||||
|
||||
sourceLink {
|
||||
path = "./"
|
||||
url = "https://github.com/InsanusMokrassar/krontab/blob/master/"
|
||||
lineSuffix = "#L"
|
||||
localDirectory.set(file("./"))
|
||||
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/krontab/blob/master/"))
|
||||
remoteLineSuffix.set("#L")
|
||||
}
|
||||
}
|
||||
|
||||
common {
|
||||
targets = ["JVM", "JS"]
|
||||
named("commonMain") {
|
||||
sourceRoot { path = "src/commonMain" }
|
||||
}
|
||||
js {
|
||||
targets = ["JS"]
|
||||
sourceRoot { path = "src/jsMain" }
|
||||
}
|
||||
jvm {
|
||||
targets = ["JVM"]
|
||||
sourceRoot { path = "src/jvmMain" }
|
||||
}
|
||||
|
||||
//
|
||||
// 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,11 +1,38 @@
|
||||
kotlin.code.style=official
|
||||
kotlin_version=1.4.0
|
||||
kotlin_coroutines_version=1.3.9
|
||||
|
||||
dokka_version=0.10.1
|
||||
|
||||
gradle_bintray_plugin_version=1.8.5
|
||||
|
||||
klockVersion=1.12.0
|
||||
|
||||
org.gradle.parallel=true
|
||||
kotlin.js.generate.externals=true
|
||||
kotlin.incremental=true
|
||||
kotlin.incremental.js=true
|
||||
kotlin.incremental.multiplatform=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=false
|
||||
|
||||
|
||||
kotlin_version=1.4.31
|
||||
kotlin_coroutines_version=1.4.3
|
||||
|
||||
dokka_version=1.4.20
|
||||
|
||||
klockVersion=2.0.7
|
||||
|
||||
## Github reease
|
||||
|
||||
github_release_plugin_version=2.2.12
|
||||
|
||||
## Android
|
||||
|
||||
android_minSdkVersion=19
|
||||
android_compileSdkVersion=30
|
||||
android_buildToolsVersion=30.0.2
|
||||
dexcount_version=2.0.0
|
||||
junit_version=4.12
|
||||
test_ext_junit_version=1.1.2
|
||||
espresso_core=3.3.0
|
||||
|
||||
androidx_work_version=2.5.0
|
||||
|
||||
## Common
|
||||
|
||||
version=0.5.1
|
||||
android_code_version=2
|
||||
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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-6.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
|
||||
|
||||
@@ -1,53 +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 {
|
||||
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"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}-mpp","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:8322/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]},"type":"Multiplatform"}
|
||||
{"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","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/krontab"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]}}
|
||||
104
publish.gradle
104
publish.gradle
@@ -1,55 +1,69 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
apply from: "maven.publish.gradle"
|
||||
task javadocsJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
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 {
|
||||
String directorySubname = it.getFile().parentFile.name
|
||||
if (it.getName() == "module.json") {
|
||||
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")
|
||||
}
|
||||
} else {
|
||||
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
|
||||
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
|
||||
} else {
|
||||
it.exclude()
|
||||
}
|
||||
publishing {
|
||||
publications.all {
|
||||
artifact javadocsJar
|
||||
|
||||
pom {
|
||||
description = "It is an analog of crontab util for Kotlin Coroutines"
|
||||
name = "Krontab"
|
||||
url = "https://github.com/InsanusMokrassar/krontab"
|
||||
|
||||
scm {
|
||||
developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/krontab.git[push=]https://github.com/InsanusMokrassar/krontab.git"
|
||||
url = "https://github.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://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
into "${project.group}".replace(".", "/")
|
||||
}
|
||||
pkg {
|
||||
repo = "InsanusMokrassar"
|
||||
name = "${project.name}-mpp"
|
||||
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')
|
||||
repositories {
|
||||
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
|
||||
maven {
|
||||
name = "GithubPackages"
|
||||
url = uri("https://maven.pkg.github.com/InsanusMokrassar/krontab")
|
||||
credentials {
|
||||
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
|
||||
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
|
||||
maven {
|
||||
name = "sonatype"
|
||||
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
|
||||
credentials {
|
||||
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
|
||||
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bintrayUpload.doFirst {
|
||||
publications = publishing.publications.collect {
|
||||
if (it.name.contains('kotlinMultiplatform')) {
|
||||
null
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
} - null
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign publishing.publications
|
||||
}
|
||||
|
||||
bintrayUpload.dependsOn publishToMavenLocal
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
|
||||
/**
|
||||
* This interface was created for abstraction of [next] operation. Currently, there is only
|
||||
* [com.insanusmokrassar.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 com.insanusmokrassar.krontab.internal.CronDateTimeScheduler
|
||||
*/
|
||||
interface KronScheduler {
|
||||
|
||||
/**
|
||||
* @return Next [DateTime] when some action must be triggered according to settings of this instance
|
||||
*
|
||||
* @see com.insanusmokrassar.krontab.internal.CronDateTimeScheduler.next
|
||||
*/
|
||||
suspend fun next(relatively: DateTime = DateTime.now()): DateTime
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.insanusmokrassar.krontab.anyCronDateTime
|
||||
import com.soywiz.klock.DateTime
|
||||
|
||||
/**
|
||||
* Cron-oriented realisation of [KronScheduler]
|
||||
*
|
||||
* @see com.insanusmokrassar.krontab.AnyTimeScheduler
|
||||
* @see com.insanusmokrassar.krontab.EverySecondScheduler
|
||||
* @see com.insanusmokrassar.krontab.EveryMinuteScheduler
|
||||
* @see com.insanusmokrassar.krontab.EveryHourScheduler
|
||||
* @see com.insanusmokrassar.krontab.EveryDayOfMonthScheduler
|
||||
* @see com.insanusmokrassar.krontab.EveryMonthScheduler
|
||||
*
|
||||
* @see com.insanusmokrassar.krontab.builder.buildSchedule
|
||||
* @see com.insanusmokrassar.krontab.builder.SchedulerBuilder
|
||||
*/
|
||||
internal 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
|
||||
private fun createSimpleScheduler(from: String, dataRange: IntRange): Array<Byte>? {
|
||||
val things = from.split(",")
|
||||
|
||||
val results = things.flatMap {
|
||||
when {
|
||||
it.contains("-") -> {
|
||||
val splitted = it.split("-")
|
||||
(splitted.first().toInt().clamp(dataRange) .. splitted[1].toInt().clamp(dataRange)).toList()
|
||||
}
|
||||
it.contains("/") -> {
|
||||
val (start, step) = it.split("/")
|
||||
val startNum = (if (start.isEmpty() || start == "*") {
|
||||
0
|
||||
} else {
|
||||
start.toInt()
|
||||
}).clamp(dataRange)
|
||||
val stepNum = step.toInt().clamp(dataRange)
|
||||
(startNum .. dataRange.last step stepNum).map { it }
|
||||
}
|
||||
it == "*" -> return null
|
||||
else -> listOf(it.toInt().clamp(dataRange))
|
||||
}
|
||||
}
|
||||
|
||||
return results.map { it.toByte() }.toTypedArray()
|
||||
}
|
||||
|
||||
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange)
|
||||
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange)
|
||||
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange)
|
||||
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange)
|
||||
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange)
|
||||
|
||||
internal fun Array<Byte>.fillWith(
|
||||
whereToPut: MutableList<CronDateTime>,
|
||||
createFactory: (CronDateTime, Byte) -> CronDateTime
|
||||
) {
|
||||
val previousValues = whereToPut.toList()
|
||||
|
||||
whereToPut.clear()
|
||||
|
||||
previousValues.forEach { previousValue ->
|
||||
forEach {
|
||||
whereToPut.add(createFactory(previousValue, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
|
||||
*
|
||||
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
|
||||
*
|
||||
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
|
||||
*/
|
||||
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
|
||||
delay((next() - DateTime.now()).millisecondsLong)
|
||||
next() ?.let {
|
||||
delay((it - DateTime.now()).millisecondsLong)
|
||||
}
|
||||
return block()
|
||||
}
|
||||
|
||||
27
src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt
Normal file
27
src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.internal.toNearDateTime
|
||||
|
||||
/**
|
||||
* This interface was created for abstraction of [next] operation. Currently, there is only
|
||||
* [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?
|
||||
}
|
||||
|
||||
suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively)
|
||||
suspend fun KronScheduler.nextOrNow(): DateTime = DateTime.now().let {
|
||||
next(it) ?: getAnyNext(it)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import dev.inmo.krontab.collection.CollectionKronScheduler
|
||||
import dev.inmo.krontab.internal.CronDateTime
|
||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
|
||||
/**
|
||||
* Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator]
|
||||
*
|
||||
* @see CollectionKronScheduler
|
||||
* @see CollectionKronScheduler.include
|
||||
*/
|
||||
fun Iterator<KronScheduler>.merge(): KronScheduler {
|
||||
val cronDateTimes = mutableListOf<CronDateTime>()
|
||||
val collectionScheduler = CollectionKronScheduler()
|
||||
forEach {
|
||||
when (it) {
|
||||
is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes)
|
||||
else -> collectionScheduler.include(it)
|
||||
}
|
||||
}
|
||||
if (cronDateTimes.isNotEmpty()) {
|
||||
collectionScheduler.include(CronDateTimeScheduler(cronDateTimes))
|
||||
}
|
||||
return collectionScheduler
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator]
|
||||
*
|
||||
* @see CollectionKronScheduler
|
||||
* @see CollectionKronScheduler.include
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun Iterable<KronScheduler>.merge(): KronScheduler = iterator().merge()
|
||||
|
||||
/**
|
||||
* @return Vararg shortcut for [merge]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.iterator().merge()
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
/**
|
||||
* This class contains [template] and can be simply serialized/deserialized. In fact that class will work as
|
||||
* [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the
|
||||
* hood
|
||||
*/
|
||||
data class KrontabTemplateWrapper(
|
||||
val template: KrontabTemplate
|
||||
) : KronScheduler by template.toKronScheduler()
|
||||
|
||||
/**
|
||||
* Will create [KrontabTemplateWrapper] from [this] [KrontabTemplate]
|
||||
*
|
||||
* @see [toKronScheduler]
|
||||
* @see [KrontabTemplateWrapper]
|
||||
*/
|
||||
fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this)
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.insanusmokrassar.krontab.builder.buildSchedule
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTime
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTimeScheduler
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.builder.buildSchedule
|
||||
import dev.inmo.krontab.internal.*
|
||||
import dev.inmo.krontab.internal.CronDateTime
|
||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
|
||||
internal val anyCronDateTime by lazy {
|
||||
CronDateTime()
|
||||
}
|
||||
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
|
||||
|
||||
/**
|
||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||
@@ -48,4 +51,11 @@ val EveryDayOfMonthScheduler: KronScheduler by lazy {
|
||||
*/
|
||||
val EveryMonthScheduler: KronScheduler by lazy {
|
||||
buildSchedule { months { 0 every 1 } }
|
||||
}
|
||||
|
||||
/**
|
||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one year
|
||||
*/
|
||||
val EveryYearScheduler: KronScheduler by lazy {
|
||||
buildSchedule { years { 0 every 1 } }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.insanusmokrassar.krontab
|
||||
package dev.inmo.krontab
|
||||
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import dev.inmo.krontab.internal.*
|
||||
|
||||
/**
|
||||
* @see createSimpleScheduler
|
||||
@@ -16,6 +16,7 @@ typealias KrontabTemplate = String
|
||||
* * hours
|
||||
* * dayOfMonth
|
||||
* * month
|
||||
* * (optional) year
|
||||
*
|
||||
* And each one have next format:
|
||||
*
|
||||
@@ -27,6 +28,8 @@ typealias KrontabTemplate = String
|
||||
* * {int}/{int}
|
||||
* * */{int}
|
||||
* * {int}
|
||||
* * F
|
||||
* * L
|
||||
*
|
||||
* Additional info about ranges can be found in follow accordance:
|
||||
*
|
||||
@@ -35,26 +38,33 @@ typealias KrontabTemplate = String
|
||||
* * Hours ranges can be found in [hoursRange]
|
||||
* * Days of month ranges can be found in [dayOfMonthRange]
|
||||
* * Months ranges can be found in [monthRange]
|
||||
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* * "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 4 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year
|
||||
*
|
||||
* @see com.insanusmokrassar.krontab.internal.createKronScheduler
|
||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||
*/
|
||||
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ")
|
||||
val yearSource: String?
|
||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
|
||||
yearSource = it.getOrNull(5)
|
||||
}
|
||||
|
||||
val secondsParsed = parseSeconds(secondsSource)
|
||||
val minutesParsed = parseMinutes(minutesSource)
|
||||
val hoursParsed = parseHours(hoursSource)
|
||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||
val monthParsed = parseMonths(monthSource)
|
||||
val yearParsed = parseYears(yearSource)
|
||||
|
||||
return createKronScheduler(
|
||||
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed
|
||||
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,4 +76,9 @@ fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleSchedu
|
||||
/**
|
||||
* Shortcut for [buildSchedule]
|
||||
*/
|
||||
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
||||
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
||||
|
||||
/**
|
||||
* Shortcut for [buildSchedule]
|
||||
*/
|
||||
fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this)
|
||||
@@ -1,15 +1,12 @@
|
||||
package com.insanusmokrassar.krontab.builder
|
||||
package dev.inmo.krontab.builder
|
||||
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTime
|
||||
import com.insanusmokrassar.krontab.internal.CronDateTimeScheduler
|
||||
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 com.insanusmokrassar.krontab.createSimpleScheduler
|
||||
* @see dev.inmo.krontab.createSimpleScheduler
|
||||
*/
|
||||
fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
|
||||
val builder = SchedulerBuilder()
|
||||
@@ -24,20 +21,21 @@ class SchedulerBuilder(
|
||||
private var minutes: Array<Byte>? = null,
|
||||
private var hours: Array<Byte>? = null,
|
||||
private var dayOfMonth: Array<Byte>? = null,
|
||||
private var month: Array<Byte>? = null
|
||||
private var month: Array<Byte>? = null,
|
||||
private var year: Array<Int>? = null
|
||||
) {
|
||||
private fun <T : TimeBuilder> callAndReturn(
|
||||
initial: Array<Byte>?,
|
||||
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
||||
initial: Array<I>?,
|
||||
builder: T,
|
||||
block: T.() -> Unit
|
||||
): Array<Byte>? {
|
||||
): List<I>? {
|
||||
builder.block()
|
||||
|
||||
val builderValue = builder.build()
|
||||
|
||||
return initial ?.let {
|
||||
builderValue ?.let { _ ->
|
||||
(it + builderValue).distinct().toTypedArray()
|
||||
(it + builderValue).distinct()
|
||||
} ?: builderValue
|
||||
} ?: builderValue
|
||||
}
|
||||
@@ -50,7 +48,7 @@ class SchedulerBuilder(
|
||||
seconds,
|
||||
SecondsBuilder(),
|
||||
block
|
||||
)
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +59,7 @@ class SchedulerBuilder(
|
||||
minutes,
|
||||
MinutesBuilder(),
|
||||
block
|
||||
)
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +70,7 @@ class SchedulerBuilder(
|
||||
hours,
|
||||
HoursBuilder(),
|
||||
block
|
||||
)
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +81,7 @@ class SchedulerBuilder(
|
||||
dayOfMonth,
|
||||
DaysOfMonthBuilder(),
|
||||
block
|
||||
)
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,14 +92,25 @@ class SchedulerBuilder(
|
||||
month,
|
||||
MonthsBuilder(),
|
||||
block
|
||||
)
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an year block
|
||||
*/
|
||||
fun years(block: YearsBuilder.() -> Unit) {
|
||||
year = callAndReturn(
|
||||
year,
|
||||
YearsBuilder(),
|
||||
block
|
||||
) ?.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Completely built and independent [KronScheduler]
|
||||
*
|
||||
* @see com.insanusmokrassar.krontab.createSimpleScheduler
|
||||
* @see com.insanusmokrassar.krontab.internal.createKronScheduler
|
||||
* @see dev.inmo.krontab.createSimpleScheduler
|
||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||
*/
|
||||
fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month)
|
||||
fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year)
|
||||
}
|
||||
@@ -1,17 +1,29 @@
|
||||
package com.insanusmokrassar.krontab.builder
|
||||
package dev.inmo.krontab.builder
|
||||
|
||||
import com.insanusmokrassar.krontab.internal.*
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
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
|
||||
sealed class TimeBuilder<T : Number> (
|
||||
private val restrictionsRange: IntRange,
|
||||
private val converter: Converter<T>
|
||||
) {
|
||||
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
|
||||
*/
|
||||
@@ -37,6 +49,13 @@ sealed class TimeBuilder (
|
||||
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, ..."
|
||||
@@ -92,11 +111,21 @@ sealed class TimeBuilder (
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
infix operator fun rangeTo(endIncluding: Int) = (this from 0) rangeTo endIncluding
|
||||
|
||||
internal fun build() = result ?.map { it.toByte() } ?.toTypedArray()
|
||||
/**
|
||||
* 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(converter)
|
||||
}
|
||||
|
||||
class SecondsBuilder : TimeBuilder(secondsRange)
|
||||
class MinutesBuilder : TimeBuilder(minutesRange)
|
||||
class HoursBuilder : TimeBuilder(hoursRange)
|
||||
class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange)
|
||||
class MonthsBuilder : TimeBuilder(monthRange)
|
||||
class SecondsBuilder : TimeBuilder<Byte>(secondsRange, intToByteConverter)
|
||||
class MinutesBuilder : TimeBuilder<Byte>(minutesRange, intToByteConverter)
|
||||
class HoursBuilder : TimeBuilder<Byte>(hoursRange, intToByteConverter)
|
||||
class DaysOfMonthBuilder : TimeBuilder<Byte>(dayOfMonthRange, intToByteConverter)
|
||||
class MonthsBuilder : TimeBuilder<Byte>(monthRange, intToByteConverter)
|
||||
class YearsBuilder : TimeBuilder<Int>(yearRange, intToIntConverter)
|
||||
@@ -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,51 @@
|
||||
package dev.inmo.krontab.collection
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.*
|
||||
import dev.inmo.krontab.internal.*
|
||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||
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 [mergeCronDateTimeSchedulers] 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(
|
||||
mergeCronDateTimeSchedulers(resultCronDateTimes)
|
||||
)
|
||||
}
|
||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||
include(it)
|
||||
}
|
||||
else -> schedulers.add(kronScheduler)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun next(relatively: DateTime): DateTime {
|
||||
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.insanusmokrassar.krontab.utils.clamp
|
||||
import com.soywiz.klock.DateTime
|
||||
import com.soywiz.klock.DateTimeSpan
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
|
||||
/**
|
||||
* @param month 0-11
|
||||
@@ -13,6 +12,7 @@ import com.soywiz.klock.DateTimeSpan
|
||||
* @param seconds 0-59
|
||||
*/
|
||||
internal data class CronDateTime(
|
||||
val year: Int? = null,
|
||||
val month: Byte? = null,
|
||||
val dayOfMonth: Byte? = null,
|
||||
val hours: Byte? = null,
|
||||
@@ -20,6 +20,7 @@ internal data class CronDateTime(
|
||||
val seconds: Byte? = null
|
||||
) {
|
||||
init {
|
||||
check(year ?.let { it in yearRange } ?: true)
|
||||
check(month ?.let { it in monthRange } ?: true)
|
||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
||||
check(hours?.let { it in hoursRange } ?: true)
|
||||
@@ -33,7 +34,7 @@ internal data class CronDateTime(
|
||||
/**
|
||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
||||
*/
|
||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime {
|
||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
||||
var current = relativelyTo
|
||||
|
||||
seconds?.let {
|
||||
@@ -52,13 +53,25 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()
|
||||
}
|
||||
|
||||
klockDayOfMonth ?.let {
|
||||
val left = it - current.dayOfMonth
|
||||
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)
|
||||
current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left)
|
||||
}
|
||||
|
||||
year ?.let {
|
||||
if (current.yearInt != it) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return current
|
||||
@@ -72,7 +85,8 @@ internal fun createKronScheduler(
|
||||
minutes: Array<Byte>? = null,
|
||||
hours: Array<Byte>? = null,
|
||||
dayOfMonth: Array<Byte>? = null,
|
||||
month: Array<Byte>? = null
|
||||
month: Array<Byte>? = null,
|
||||
years: Array<Int>? = null
|
||||
): KronScheduler {
|
||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||
|
||||
@@ -96,5 +110,9 @@ internal fun createKronScheduler(
|
||||
previousCronDateTime.copy(month = currentTime)
|
||||
}
|
||||
|
||||
years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
||||
previousCronDateTime.copy(year = currentTime)
|
||||
}
|
||||
|
||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.*
|
||||
import dev.inmo.krontab.collection.plus
|
||||
|
||||
/**
|
||||
* 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.EveryYearScheduler
|
||||
*
|
||||
* @see dev.inmo.krontab.builder.buildSchedule
|
||||
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
||||
*/
|
||||
internal data class CronDateTimeScheduler internal constructor(
|
||||
internal val cronDateTimes: List<CronDateTime>
|
||||
) : KronScheduler {
|
||||
/**
|
||||
* @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one
|
||||
*
|
||||
* @see toNearDateTime
|
||||
*/
|
||||
override suspend fun next(relatively: DateTime): DateTime {
|
||||
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
||||
schedulers.flatMap { it.cronDateTimes }
|
||||
)
|
||||
|
||||
/**
|
||||
* @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of
|
||||
* [kronSchedulers] included
|
||||
*/
|
||||
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
|
||||
fun merge(kronSchedulers: List<KronScheduler>) = kronSchedulers.apply { dev.inmo.krontab.merge() }
|
||||
|
||||
/**
|
||||
* @return Vararg shortcut for [dev.inmo.krontab.merge]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
|
||||
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.apply { dev.inmo.krontab.merge() }
|
||||
/**
|
||||
* @return Vararg shortcut for [dev.inmo.krontab.merge]
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
|
||||
inline fun KronScheduler.plus(other: KronScheduler) = this + other
|
||||
62
src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt
Normal file
62
src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
import dev.inmo.krontab.utils.clamp
|
||||
|
||||
typealias Converter<T> = (Int) -> T
|
||||
|
||||
internal val intToByteConverter: Converter<Byte> = { it: Int -> it.toByte() }
|
||||
internal val intToIntConverter: Converter<Int> = { it: Int -> it }
|
||||
private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataConverter: Converter<T>): List<T>? {
|
||||
val things = from.split(",")
|
||||
|
||||
val results = things.flatMap {
|
||||
val currentToken = it.toLowerCase().replace(
|
||||
"f", dataRange.first.toString()
|
||||
).replace(
|
||||
"l", dataRange.last.toString()
|
||||
)
|
||||
when {
|
||||
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 {
|
||||
start.toInt()
|
||||
}).clamp(dataRange)
|
||||
val stepNum = step.toInt().clamp(dataRange)
|
||||
(startNum .. dataRange.last step stepNum).map { it }
|
||||
}
|
||||
currentToken == "*" -> return null
|
||||
else -> listOf(currentToken.toInt().clamp(dataRange))
|
||||
}
|
||||
}
|
||||
|
||||
return results.map(dataConverter)
|
||||
}
|
||||
|
||||
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
||||
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
||||
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
||||
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange, intToByteConverter) ?.toTypedArray()
|
||||
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange, intToByteConverter) ?.toTypedArray()
|
||||
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange, intToByteConverter) ?.toTypedArray()
|
||||
|
||||
internal fun <T> Array<T>.fillWith(
|
||||
whereToPut: MutableList<CronDateTime>,
|
||||
createFactory: (CronDateTime, T) -> CronDateTime
|
||||
) {
|
||||
val previousValues = whereToPut.toList()
|
||||
|
||||
whereToPut.clear()
|
||||
|
||||
previousValues.forEach { previousValue ->
|
||||
forEach {
|
||||
whereToPut.add(createFactory(previousValue, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.insanusmokrassar.krontab.internal
|
||||
package dev.inmo.krontab.internal
|
||||
|
||||
internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE
|
||||
internal val monthRange = 0 .. 11
|
||||
internal val dayOfMonthRange = 0 .. 30
|
||||
internal val hoursRange = 0 .. 23
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
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]
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import com.insanusmokrassar.krontab.KronScheduler
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.KronScheduler
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -17,7 +17,7 @@ class SchedulerFlow(
|
||||
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
|
||||
while (true) {
|
||||
val now = DateTime.now()
|
||||
val nextTime = scheduler.next(now)
|
||||
val nextTime = scheduler.next(now) ?: break
|
||||
val sleepDelay = (nextTime - now).millisecondsLong
|
||||
delay(sleepDelay)
|
||||
collector.emit(nextTime)
|
||||
@@ -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
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.insanusmokrassar.krontab.utils
|
||||
package dev.inmo.krontab.utils
|
||||
|
||||
import com.insanusmokrassar.krontab.buildSchedule
|
||||
import com.soywiz.klock.DateTime
|
||||
import dev.inmo.krontab.buildSchedule
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@@ -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
|
||||
1
src/main/AndroidManifest.xml
Normal file
1
src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="dev.inmo.krontab"/>
|
||||
147
src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt
Normal file
147
src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt
Normal file
@@ -0,0 +1,147 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
import com.soywiz.klock.DateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* This method will enqueue [OneTimeWorkRequest] with [workName] and [existingWorkPolicy]. Use [setUpRequest] callback
|
||||
* in case you need some additional actions to do before request will be enqueued
|
||||
*/
|
||||
suspend fun <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||
workName: String,
|
||||
delayMillis: Long,
|
||||
workClass: Class<T>,
|
||||
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||
) = WorkManager.getInstance(applicationContext).enqueueUniqueWork(
|
||||
workName,
|
||||
existingWorkPolicy,
|
||||
OneTimeWorkRequest.Builder(workClass).apply {
|
||||
setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
|
||||
setUpRequest()
|
||||
}.build()
|
||||
)
|
||||
/**
|
||||
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T] parameter
|
||||
*/
|
||||
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||
workName: String,
|
||||
delayMillis: Long,
|
||||
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||
) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, existingWorkPolicy, setUpRequest)
|
||||
|
||||
/**
|
||||
* This method is shortcut for [enqueueKronSchedulerWork] with [initialScheduler]. It will try to calculate delay by
|
||||
* itself. In case if [KronScheduler.next] of [initialScheduler] will return null, work WILL NOT be enqueued
|
||||
*
|
||||
* @return null in case if [KronScheduler.next] of [initialScheduler] has returned null and work has not been enqueued
|
||||
*/
|
||||
suspend fun <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||
workName: String,
|
||||
initialScheduler: KronScheduler,
|
||||
workClass: Class<T>,
|
||||
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||
): Operation? {
|
||||
val now = DateTime.now()
|
||||
val nextTriggerTime = initialScheduler.next(now)
|
||||
val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return null
|
||||
|
||||
return enqueueKronSchedulerWork(workName, delayMillis, workClass, existingWorkPolicy, setUpRequest)
|
||||
}
|
||||
/**
|
||||
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T]
|
||||
*/
|
||||
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
|
||||
workName: String,
|
||||
initialScheduler: KronScheduler,
|
||||
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
|
||||
) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, existingWorkPolicy, setUpRequest)
|
||||
|
||||
/**
|
||||
* Use this class as a super class in case you wish to implement krontab-based enqueuing of works
|
||||
*
|
||||
* @see enqueueKronSchedulerWork
|
||||
* @see KrontabTemplateSchedulerWork
|
||||
*/
|
||||
abstract class KronSchedulerWork(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : CoroutineWorker(
|
||||
context,
|
||||
workerParams
|
||||
) {
|
||||
/**
|
||||
* This variable will be used to reorder new work after that one is happen
|
||||
*
|
||||
* @see prolongOnException
|
||||
* @see prolongOnFailure
|
||||
* @see prolongOnSuccess
|
||||
*/
|
||||
protected abstract val workName: String
|
||||
|
||||
/**
|
||||
* Set this to false in case when this work must not be enqueued after successful complete
|
||||
*/
|
||||
protected open val prolongOnSuccess: Boolean = true
|
||||
/**
|
||||
* Set this to false in case when this work must not be enqueued after failure complete
|
||||
*/
|
||||
protected open val prolongOnFailure
|
||||
get() = prolongOnSuccess
|
||||
/**
|
||||
* Set this to false in case when this work must not be enqueued after exception happen
|
||||
*/
|
||||
protected open val prolongOnException = false
|
||||
|
||||
/**
|
||||
* [KronScheduler] of this method will be used to [prolong] this worker
|
||||
*/
|
||||
protected abstract suspend fun kronScheduler(): KronScheduler?
|
||||
|
||||
/**
|
||||
* This method is replacement of [doWork]. It is required to wrap work with [prolong]ing and handling of complete
|
||||
* state
|
||||
*/
|
||||
protected abstract suspend fun onWork(): Result
|
||||
|
||||
/**
|
||||
* Override this method in case you have some additional settings for future [OneTimeWorkRequest]
|
||||
*/
|
||||
protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest() {}
|
||||
|
||||
/**
|
||||
* This method will [enqueueKronSchedulerWork] using [workName], [kronScheduler] and default
|
||||
* [ExistingWorkPolicy.REPLACE]. You can call this method in case you want to enqueue work by yourself, but you must
|
||||
* be sure that you set up to false [prolongOnSuccess], [prolongOnFailure] and [prolongOnException]
|
||||
*/
|
||||
protected suspend fun prolong() {
|
||||
applicationContext.enqueueKronSchedulerWork(
|
||||
workName,
|
||||
kronScheduler() ?: return,
|
||||
this::class.java
|
||||
) {
|
||||
setUpRequest()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val result = try {
|
||||
onWork()
|
||||
} catch (e: Throwable) {
|
||||
if (prolongOnException) {
|
||||
prolong()
|
||||
}
|
||||
throw e
|
||||
}
|
||||
when (result) {
|
||||
is Result.Failure -> if (prolongOnFailure) prolong()
|
||||
is Result.Success -> if (prolongOnSuccess) prolong()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.inmo.krontab
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.*
|
||||
|
||||
const val krontabTemplateWorkField = "krontabTemplate"
|
||||
|
||||
/**
|
||||
* Will [enqueueKronSchedulerWork] with [KronScheduler] from [krontabTemplate] and call [setUpRequest] on setting up
|
||||
* [OneTimeWorkRequest.Builder] with [Data] which will be used to [OneTimeWorkRequest.Builder.setInputData] after
|
||||
* [setUpRequest] completed
|
||||
*/
|
||||
suspend inline fun <reified T : KrontabTemplateSchedulerWork> Context.enqueueKrontabTemplateSchedulerWork(
|
||||
workName: String,
|
||||
krontabTemplate: KrontabTemplate,
|
||||
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.(
|
||||
data: Data
|
||||
) -> Unit = {}
|
||||
) = enqueueKronSchedulerWork(workName, krontabTemplate.toKronScheduler(), T::class.java, existingWorkPolicy) {
|
||||
val data = workDataOf(
|
||||
krontabTemplateWorkField to krontabTemplate
|
||||
)
|
||||
setUpRequest(data)
|
||||
setInputData(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this class in case you wish to base on [KrontabTemplate]. It will automatically handle request of
|
||||
* [kronScheduler] and put it in [setUpRequest]
|
||||
*/
|
||||
abstract class KrontabTemplateSchedulerWork(
|
||||
context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : KronSchedulerWork(context, workerParams) {
|
||||
/**
|
||||
* Will try to get [KrontabTemplate] from [getInputData] by key [krontabTemplateWorkField]
|
||||
*
|
||||
* @see setUpRequest
|
||||
*/
|
||||
protected val krontabTemplate: KrontabTemplate?
|
||||
get() = inputData.getString(krontabTemplateWorkField)
|
||||
|
||||
/**
|
||||
* Override this methods instead of old [setUpRequest] in case you wish to set up some work request parameters
|
||||
*
|
||||
* @param data This parameter will be used to put data inside of [OneTimeWorkRequest.Builder] after this method
|
||||
* will be completed
|
||||
*/
|
||||
protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest(data: Data) {}
|
||||
|
||||
/**
|
||||
* Will automatically put [krontabTemplate] into work data, call [setUpRequest] with future [Data] object and then
|
||||
* call [OneTimeWorkRequest.Builder.setInputData] with that [Data] object
|
||||
*/
|
||||
override suspend fun OneTimeWorkRequest.Builder.setUpRequest() {
|
||||
val data = workDataOf(
|
||||
krontabTemplateWorkField to krontabTemplate,
|
||||
)
|
||||
setUpRequest(data)
|
||||
setInputData(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return [KronScheduler] in case if [krontabTemplate] was not null
|
||||
*/
|
||||
override suspend fun kronScheduler(): KronScheduler? = krontabTemplate ?.toKronScheduler()
|
||||
}
|
||||
Reference in New Issue
Block a user