Merge pull request #63 from InsanusMokrassar/2.1.0

2.1.0
This commit is contained in:
InsanusMokrassar 2023-06-20 10:42:01 +06:00 committed by GitHub
commit 7807d10121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 272 additions and 83 deletions

View File

@ -1,5 +1,16 @@
# Changelog # Changelog
## 2.1.0
* Versions
* `Kotlin`: `1.8.22`
* `Klock`: `4.0.3`
* New value class `KrontabConfig`. It is preferable way to create `KronScheduler` instead of
`KrontabTemplate` since this update
* You may configure krontab with builders using simple `KronScheduler` invoke extension
* New useful extensions like `KronScheduler.daily`
* `KrontabTemplateWrapper` is obsolete in favor to `KrontabConfig`
## 2.0.0 ## 2.0.0
* Versions * Versions

View File

@ -8,6 +8,7 @@ buildscript {
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version" classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
classpath "com.android.tools.build:gradle:$android_gradle_version" classpath "com.android.tools.build:gradle:$android_gradle_version"
@ -16,6 +17,7 @@ buildscript {
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
id "org.jetbrains.dokka" version "$dokka_version" id "org.jetbrains.dokka" version "$dokka_version"
} }
@ -66,6 +68,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('stdlib') implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlin_serialization_version"
api "com.soywiz.korlibs.klock:klock:$klockVersion" api "com.soywiz.korlibs.klock:klock:$klockVersion"
} }

View File

@ -9,12 +9,13 @@ android.useAndroidX=true
android.enableJetifier=false android.enableJetifier=false
kotlin_version=1.8.21 kotlin_version=1.8.22
kotlin_coroutines_version=1.6.4 kotlin_coroutines_version=1.6.4
kotlin_serialization_version=1.5.1
dokka_version=1.8.20 dokka_version=1.8.20
klockVersion=4.0.1 klockVersion=4.0.3
## Github reease ## Github reease
@ -35,5 +36,5 @@ androidx_work_version=2.8.1
## Common ## Common
version=2.0.0 version=2.1.0
android_code_version=26 android_code_version=27

View File

@ -18,6 +18,8 @@ interface KronScheduler {
* @see dev.inmo.krontab.internal.CronDateTimeScheduler.next * @see dev.inmo.krontab.internal.CronDateTimeScheduler.next
*/ */
suspend fun next(relatively: DateTime = DateTime.now()): DateTime? suspend fun next(relatively: DateTime = DateTime.now()): DateTime?
companion object
} }
suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively)

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ import dev.inmo.krontab.utils.Minutes
* *
* @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.createSimpleScheduler
*/ */
fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler { inline fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
val builder = SchedulerBuilder() val builder = SchedulerBuilder()
builder.settingsBlock() builder.settingsBlock()
@ -27,7 +27,7 @@ fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
* *
* @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.createSimpleScheduler
*/ */
fun buildSchedule( inline fun buildSchedule(
offset: Minutes, offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz { ): KronSchedulerTz {
@ -38,6 +38,27 @@ fun buildSchedule(
return builder.build() as KronSchedulerTz return builder.build() as KronSchedulerTz
} }
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz = buildSchedule(offset, settingsBlock)
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
settingsBlock: SchedulerBuilder.() -> Unit
): KronScheduler = buildSchedule(settingsBlock)
class SchedulerBuilder( class SchedulerBuilder(
private var seconds: Array<Byte>? = null, private var seconds: Array<Byte>? = null,
private var minutes: Array<Byte>? = null, private var minutes: Array<Byte>? = null,