mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-19 15:29:24 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
3a5771a0cc | |||
527a2a91ac | |||
6763e5c4c6 | |||
06918d8310 | |||
89ccaa1b57 | |||
5d0bdb9bcf | |||
31fdcf74a5 | |||
afca09cc1d | |||
531d89d9db | |||
6bbbea0bc3 | |||
e337cd98c8 | |||
bcbab3b380 | |||
fb63de7568 | |||
aa45a4ab13 | |||
2af7e2f681 | |||
34fd9edce0 | |||
2a4cb8c5f9 | |||
50ea40bc3a | |||
a77654052d | |||
88aafce552 | |||
4e95d6bfff |
29
CHANGELOG.md
29
CHANGELOG.md
@@ -1,5 +1,34 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.8.5
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* `repeatOnFailure`
|
||||||
|
|
||||||
|
## 0.8.4
|
||||||
|
|
||||||
|
* `Ktor`:
|
||||||
|
* `Server`:
|
||||||
|
* Several new `createKtorServer`
|
||||||
|
|
||||||
|
## 0.8.3
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* Ranges intersection functionality
|
||||||
|
* New type `Optional`
|
||||||
|
* `Pagination`:
|
||||||
|
* `Pagination` now extends `ClosedRange<Int>`
|
||||||
|
* `Pagination` intersection functionality
|
||||||
|
|
||||||
|
## 0.8.2
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Klock`: `2.4.7` -> `2.4.8`
|
||||||
|
* `Serialization`: `1.3.0` -> `1.3.1`
|
||||||
|
* `FSM`:
|
||||||
|
* Now it is possible to pass any `CheckableHandlerHolder` in `FSMBuilder`
|
||||||
|
* Now `StatesMachine` works with `CheckableHandlerHolder` instead of `CustomizableHandlerHolder`
|
||||||
|
|
||||||
## 0.8.1
|
## 0.8.1
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type represents [T] as not only potentially nullable data, but also as a data which can not be presented. This
|
||||||
|
* type will be useful in cases when [T] is nullable and null as valuable data too in time of data absence should be
|
||||||
|
* presented by some third type.
|
||||||
|
*
|
||||||
|
* Let's imagine, you have nullable name in some database. In case when name is not nullable everything is clear - null
|
||||||
|
* will represent absence of row in the database. In case when name is nullable null will be a little bit dual-meaning,
|
||||||
|
* cause this null will say nothing about availability of the row (of course, it is exaggerated example)
|
||||||
|
*
|
||||||
|
* @see Optional.presented
|
||||||
|
* @see Optional.absent
|
||||||
|
* @see Optional.optional
|
||||||
|
* @see Optional.onPresented
|
||||||
|
* @see Optional.onAbsent
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Optional<T> internal constructor(
|
||||||
|
internal val data: T?,
|
||||||
|
internal val dataPresented: Boolean
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Will create [Optional] with presented data
|
||||||
|
*/
|
||||||
|
fun <T> presented(data: T) = Optional(data, true)
|
||||||
|
/**
|
||||||
|
* Will create [Optional] without data
|
||||||
|
*/
|
||||||
|
fun <T> absent() = Optional<T>(null, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline val <T> T.optional
|
||||||
|
get() = Optional.presented(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will call [block] when data presented ([Optional.dataPresented] == true)
|
||||||
|
*/
|
||||||
|
fun <T> Optional<T>.onPresented(block: (T) -> Unit): Optional<T> = apply {
|
||||||
|
if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will call [block] when data absent ([Optional.dataPresented] == false)
|
||||||
|
*/
|
||||||
|
fun <T> Optional<T>.onAbsent(block: () -> Unit): Optional<T> = apply {
|
||||||
|
if (!dataPresented) { block() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or null otherwise
|
||||||
|
*/
|
||||||
|
fun <T> Optional<T>.dataOrNull() = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or throw [throwable] otherwise
|
||||||
|
*/
|
||||||
|
fun <T> Optional<T>.dataOrThrow(throwable: Throwable) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else throw throwable
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
|
||||||
|
*/
|
||||||
|
fun <T> Optional<T>.dataOrElse(block: () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
|
||||||
|
*/
|
||||||
|
suspend fun <T> Optional<T>.dataOrElseSuspendable(block: suspend () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
|
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> ClosedRange<T>.intersect(other: ClosedRange<T>): Pair<T, T>? = when {
|
||||||
|
start == other.start && endInclusive == other.endInclusive -> start to endInclusive
|
||||||
|
start > other.endInclusive || other.start > endInclusive -> null
|
||||||
|
else -> maxOf(start, other.start) to minOf(endInclusive, other.endInclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IntRange.intersect(
|
||||||
|
other: IntRange
|
||||||
|
): IntRange? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
|
||||||
|
it.first .. it.second
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LongRange.intersect(
|
||||||
|
other: LongRange
|
||||||
|
): LongRange? = (this as ClosedRange<Long>).intersect(other as ClosedRange<Long>) ?.let {
|
||||||
|
it.first .. it.second
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given [action] until getting of successful result specified number of [times].
|
||||||
|
*
|
||||||
|
* A zero-based index of current iteration is passed as a parameter to [action].
|
||||||
|
*/
|
||||||
|
inline fun <R> repeatOnFailure(
|
||||||
|
times: Int,
|
||||||
|
onEachFailure: (Throwable) -> Unit = {},
|
||||||
|
action: (Int) -> R
|
||||||
|
): Optional<R> {
|
||||||
|
repeat(times) {
|
||||||
|
runCatching {
|
||||||
|
action(it)
|
||||||
|
}.onFailure(onEachFailure).onSuccess {
|
||||||
|
return Optional.presented(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.absent()
|
||||||
|
}
|
@@ -12,7 +12,7 @@ import kotlinx.coroutines.*
|
|||||||
interface StatesMachine<T : State> : StatesHandler<T, T> {
|
interface StatesMachine<T : State> : StatesHandler<T, T> {
|
||||||
suspend fun launchStateHandling(
|
suspend fun launchStateHandling(
|
||||||
state: T,
|
state: T,
|
||||||
handlers: List<CustomizableHandlerHolder<in T, T>>
|
handlers: List<CheckableHandlerHolder<in T, T>>
|
||||||
): T? {
|
): T? {
|
||||||
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
||||||
handleState(state)
|
handleState(state)
|
||||||
@@ -35,7 +35,7 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
|||||||
*/
|
*/
|
||||||
operator fun <T: State> invoke(
|
operator fun <T: State> invoke(
|
||||||
statesManager: StatesManager<T>,
|
statesManager: StatesManager<T>,
|
||||||
handlers: List<CustomizableHandlerHolder<in T, T>>
|
handlers: List<CheckableHandlerHolder<in T, T>>
|
||||||
) = DefaultStatesMachine(statesManager, handlers)
|
) = DefaultStatesMachine(statesManager, handlers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
|||||||
*/
|
*/
|
||||||
class DefaultStatesMachine <T: State>(
|
class DefaultStatesMachine <T: State>(
|
||||||
private val statesManager: StatesManager<T>,
|
private val statesManager: StatesManager<T>,
|
||||||
private val handlers: List<CustomizableHandlerHolder<in T, T>>
|
private val handlers: List<CheckableHandlerHolder<in T, T>>
|
||||||
) : StatesMachine<T> {
|
) : StatesMachine<T> {
|
||||||
/**
|
/**
|
||||||
* Will call [launchStateHandling] for state handling
|
* Will call [launchStateHandling] for state handling
|
||||||
|
@@ -9,14 +9,18 @@ class FSMBuilder<T : State>(
|
|||||||
var statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
var statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
|
||||||
var defaultStateHandler: StatesHandler<T, T>? = StatesHandler { null }
|
var defaultStateHandler: StatesHandler<T, T>? = StatesHandler { null }
|
||||||
) {
|
) {
|
||||||
private var states = mutableListOf<CustomizableHandlerHolder<T, T>>()
|
private var states = mutableListOf<CheckableHandlerHolder<T, T>>()
|
||||||
|
|
||||||
|
fun add(handler: CheckableHandlerHolder<T, T>) {
|
||||||
|
states.add(handler)
|
||||||
|
}
|
||||||
|
|
||||||
fun <I : T> add(kClass: KClass<I>, handler: StatesHandler<I, T>) {
|
fun <I : T> add(kClass: KClass<I>, handler: StatesHandler<I, T>) {
|
||||||
states.add(CheckableHandlerHolder(kClass, false, handler))
|
add(CheckableHandlerHolder(kClass, false, handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <I : T> add(filter: suspend (state: State) -> Boolean, handler: StatesHandler<I, T>) {
|
fun <I : T> add(filter: suspend (state: State) -> Boolean, handler: StatesHandler<I, T>) {
|
||||||
states.add(handler.holder(filter))
|
add(handler.holder(filter))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <I : T> addStrict(kClass: KClass<I>, handler: StatesHandler<I, T>) {
|
fun <I : T> addStrict(kClass: KClass<I>, handler: StatesHandler<I, T>) {
|
||||||
|
@@ -9,12 +9,12 @@ org.gradle.jvmargs=-Xmx2g
|
|||||||
|
|
||||||
kotlin_version=1.5.31
|
kotlin_version=1.5.31
|
||||||
kotlin_coroutines_version=1.5.2
|
kotlin_coroutines_version=1.5.2
|
||||||
kotlin_serialisation_core_version=1.3.0
|
kotlin_serialisation_core_version=1.3.1
|
||||||
kotlin_exposed_version=0.36.2
|
kotlin_exposed_version=0.36.2
|
||||||
|
|
||||||
ktor_version=1.6.5
|
ktor_version=1.6.5
|
||||||
|
|
||||||
klockVersion=2.4.7
|
klockVersion=2.4.8
|
||||||
|
|
||||||
github_release_plugin_version=2.2.12
|
github_release_plugin_version=2.2.12
|
||||||
|
|
||||||
@@ -45,5 +45,5 @@ dokka_version=1.5.31
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.8.1
|
version=0.8.5
|
||||||
android_code_version=81
|
android_code_version=85
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.ktor.server
|
package dev.inmo.micro_utils.ktor.server
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.server.cio.CIO
|
import io.ktor.server.cio.CIO
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
@@ -31,3 +32,27 @@ fun createKtorServer(
|
|||||||
port: Int = Random.nextInt(1024, 65535),
|
port: Int = Random.nextInt(1024, 65535),
|
||||||
block: Application.() -> Unit
|
block: Application.() -> Unit
|
||||||
): ApplicationEngine = createKtorServer(CIO, host, port, block)
|
): ApplicationEngine = createKtorServer(CIO, host, port, block)
|
||||||
|
|
||||||
|
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> createKtorServer(
|
||||||
|
engine: ApplicationEngineFactory<TEngine, TConfiguration>,
|
||||||
|
host: String = "localhost",
|
||||||
|
port: Int = Random.nextInt(1024, 65535),
|
||||||
|
configurators: List<KtorApplicationConfigurator>
|
||||||
|
): TEngine = createKtorServer(
|
||||||
|
engine,
|
||||||
|
host,
|
||||||
|
port
|
||||||
|
) {
|
||||||
|
configurators.forEach { it.apply { configure() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create server with [CIO] server engine without starting of it
|
||||||
|
*
|
||||||
|
* @see ApplicationEngine.start
|
||||||
|
*/
|
||||||
|
fun createKtorServer(
|
||||||
|
host: String = "localhost",
|
||||||
|
port: Int = Random.nextInt(1024, 65535),
|
||||||
|
configurators: List<KtorApplicationConfigurator>
|
||||||
|
): ApplicationEngine = createKtorServer(CIO, host, port, configurators)
|
||||||
|
52
mime_types/mimes_generator/mime_generator.py
Normal file
52
mime_types/mimes_generator/mime_generator.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import pandas as pd
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
def fix_name(category, raw_name):
|
||||||
|
splitted = raw_name.replace('-', '+').replace('.', '+').replace(',', '+').split('+')
|
||||||
|
out1 = ""
|
||||||
|
for s in splitted:
|
||||||
|
out1 += s.capitalize()
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
if out1[0].isdigit():
|
||||||
|
result += category[0].capitalize()
|
||||||
|
result += out1
|
||||||
|
else:
|
||||||
|
result += out1
|
||||||
|
return result
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
df = pd.read_html(open('table.html', 'r'))
|
||||||
|
mimes = []
|
||||||
|
for row in df[0].iterrows():
|
||||||
|
mime = row[1][1]
|
||||||
|
mime_category = mime.split('/', 1)[0]
|
||||||
|
mime_name = mime.split('/', 1)[1]
|
||||||
|
mimes.append({
|
||||||
|
'mime_category': mime_category,
|
||||||
|
'mime_name': mime_name,
|
||||||
|
})
|
||||||
|
|
||||||
|
# codegen
|
||||||
|
|
||||||
|
mimes.sort(key=lambda x: x['mime_category'])
|
||||||
|
grouped = itertools.groupby(mimes, lambda x: x['mime_category'])
|
||||||
|
code = ''
|
||||||
|
code2 = 'internal val knownMimeTypes: Set<MimeType> = setOf(\n'
|
||||||
|
code2 += ' KnownMimeTypes.Any,\n'
|
||||||
|
for key, group in grouped:
|
||||||
|
group_name = key.capitalize()
|
||||||
|
code += '@Serializable(MimeTypeSerializer::class)\nsealed class %s(raw: String) : MimeType, KnownMimeTypes(raw) {\n' % group_name
|
||||||
|
code += ' @Serializable(MimeTypeSerializer::class)\n object Any: %s ("%s/*")\n' % (group_name, key)
|
||||||
|
for mime in group:
|
||||||
|
name = fix_name(mime['mime_category'], mime['mime_name'])
|
||||||
|
code += ' @Serializable(MimeTypeSerializer::class)\n object %s: %s ("%s/%s")\n' % (name, group_name, mime['mime_category'], mime['mime_name'])
|
||||||
|
code2 += ' KnownMimeTypes.%s.%s,\n' % (group_name, name)
|
||||||
|
code += '}\n\n'
|
||||||
|
code2 += ')\n'
|
||||||
|
with open('out1.txt', 'w') as file:
|
||||||
|
file.write(code)
|
||||||
|
with open('out2.txt', 'w') as file:
|
||||||
|
file.write(code2)
|
@@ -5,3 +5,13 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.common")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.pagination
|
package dev.inmo.micro_utils.pagination
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.intersect
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ import kotlin.math.floor
|
|||||||
* If you want to request something, you should use [SimplePagination]. If you need to return some result including
|
* If you want to request something, you should use [SimplePagination]. If you need to return some result including
|
||||||
* pagination - [PaginationResult]
|
* pagination - [PaginationResult]
|
||||||
*/
|
*/
|
||||||
interface Pagination {
|
interface Pagination : ClosedRange<Int> {
|
||||||
/**
|
/**
|
||||||
* Started with 0.
|
* Started with 0.
|
||||||
* Number of page inside of pagination. Offset can be calculated as [page] * [size]
|
* Number of page inside of pagination. Offset can be calculated as [page] * [size]
|
||||||
@@ -20,6 +21,17 @@ interface Pagination {
|
|||||||
* Size of current page. Offset can be calculated as [page] * [size]
|
* Size of current page. Offset can be calculated as [page] * [size]
|
||||||
*/
|
*/
|
||||||
val size: Int
|
val size: Int
|
||||||
|
|
||||||
|
override val start: Int
|
||||||
|
get() = page * size
|
||||||
|
override val endInclusive: Int
|
||||||
|
get() = start + size - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Pagination.intersect(
|
||||||
|
other: Pagination
|
||||||
|
): Pagination? = (this as ClosedRange<Int>).intersect(other as ClosedRange<Int>) ?.let {
|
||||||
|
PaginationByIndexes(it.first, it.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +44,7 @@ inline val Pagination.isFirstPage
|
|||||||
* First number in index of objects. It can be used as offset for databases or other data sources
|
* First number in index of objects. It can be used as offset for databases or other data sources
|
||||||
*/
|
*/
|
||||||
val Pagination.firstIndex: Int
|
val Pagination.firstIndex: Int
|
||||||
get() = page * size
|
get() = start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
|
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
|
||||||
@@ -41,7 +53,7 @@ val Pagination.firstIndex: Int
|
|||||||
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19. Here [Pagination.lastIndexExclusive] == 20
|
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19. Here [Pagination.lastIndexExclusive] == 20
|
||||||
*/
|
*/
|
||||||
val Pagination.lastIndexExclusive: Int
|
val Pagination.lastIndexExclusive: Int
|
||||||
get() = firstIndex + size
|
get() = endInclusive + 1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
|
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
|
||||||
@@ -50,7 +62,7 @@ val Pagination.lastIndexExclusive: Int
|
|||||||
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
|
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
|
||||||
*/
|
*/
|
||||||
val Pagination.lastIndex: Int
|
val Pagination.lastIndex: Int
|
||||||
get() = lastIndexExclusive - 1
|
get() = endInclusive
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates pages count for given [datasetSize]
|
* Calculates pages count for given [datasetSize]
|
||||||
|
Reference in New Issue
Block a user