Compare commits

...

9 Commits
0.4.5 ... 0.4.6

Author SHA1 Message Date
091bb1394f Warning annotation 2020-11-26 18:01:12 +06:00
b82c3864a0 typealiases for repos 2020-11-26 15:32:00 +06:00
49ee38a936 FileStandardKeyValueRepo 2020-11-26 15:03:40 +06:00
c201866c51 FlowCollector#invoke 2020-11-26 15:02:30 +06:00
023f841fb5 Pagination#isFirstPage 2020-11-26 15:01:45 +06:00
76102e9ab3 start 0.4.6 2020-11-26 15:00:32 +06:00
b2fc5e2a4d add query params docs 2020-11-25 11:09:54 +06:00
55aacb8753 update publish script 2020-11-23 22:46:40 +06:00
8702846216 Merge pull request #22 from InsanusMokrassar/0.4.5
0.4.5
2020-11-23 18:48:57 +06:00
12 changed files with 249 additions and 4 deletions

View File

@@ -1,5 +1,20 @@
# Changelog
## 0.4.6
* `Common`
* New annotation `Warning` has been added
* `Pagination`
* `Common`
* `Pagination` got new extension: `Pagination#isFirstPage`
* `Coroutines`:
* New extension `FlowCollector#invoke` has been added
* `Repos`
* `Common`
* `JVM` (and `Android` since `Android API 26`):
* `FileStandardKeyValueRepo` has been added
* Add several `typealias`es for each type of repos
## 0.4.5
* `Android`

View File

@@ -17,3 +17,21 @@ package dev.inmo.micro_utils.common
AnnotationTarget.TYPE_PARAMETER
)
annotation class PreviewFeature
@RequiresOptIn(
"This thing is marked as warned. See message of warn to get more info",
RequiresOptIn.Level.WARNING
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE,
AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER
)
annotation class Warning(val message: String)

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.FlowCollector
suspend inline operator fun <T> FlowCollector<T>.invoke(value: T) = emit(value)

View File

@@ -41,5 +41,5 @@ dokka_version=1.4.0
# Project data
group=dev.inmo
version=0.4.5
android_code_version=9
version=0.4.6
android_code_version=10

View File

@@ -3,16 +3,28 @@ package dev.inmo.micro_utils.ktor.common
typealias QueryParam = Pair<String, String?>
typealias QueryParams = Map<String, String?>
/**
* Create query part which includes key=value pairs separated with &
*/
val QueryParams.asUrlQuery: String
get() = keys.joinToString("&") { "${it}${get(it) ?.let { value -> "=$value" }}" }
/**
* Create query part which includes key=value pairs separated with &
*/
val List<QueryParam>.asUrlQuery: String
get() = joinToString("&") { (key, value) -> "${key}${value ?.let { _ -> "=$value" }}" }
/**
* Create query part which includes key=value pairs separated with & and attach to receiver
*/
fun String.includeQueryParams(
queryParams: QueryParams
): String = "$this${if(queryParams.isNotEmpty()) "${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" else ""}"
/**
* Create query part which includes key=value pairs separated with & and attach to receiver
*/
fun String.includeQueryParams(
queryParams: List<QueryParam>
): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}"

View File

@@ -22,6 +22,12 @@ interface Pagination {
val size: Int
}
/**
* Logical shortcut for comparison that page is 0
*/
inline val Pagination.isFirstPage
get() = page == 0
/**
* First number in index of objects. It can be used as offset for databases or other data sources
*/

View File

@@ -3,6 +3,9 @@ apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
task sourceJar (type : Jar) {
classifier = 'sources'
}
afterEvaluate {
project.publishing.publications.all {
@@ -10,6 +13,7 @@ afterEvaluate {
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) {
artifactId = "${project.name}"
artifact sourceJar
} else {
artifactId = "${project.name}-$name"
}

View File

@@ -17,6 +17,11 @@ kotlin {
}
}
jvmMain {
dependencies {
api internalProject("micro_utils.common")
}
}
androidMain {
dependencies {
api "androidx.core:core-ktx:$core_ktx_version"

View File

@@ -36,6 +36,7 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
}
}
}
typealias ReadKeyValuesRepo<Key,Value> = ReadOneToManyKeyValueRepo<Key, Value>
interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
@@ -53,6 +54,7 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
add(toSet)
}
}
typealias WriteKeyValuesRepo<Key,Value> = WriteOneToManyKeyValueRepo<Key, Value>
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
keysAndValues: List<Pair<Key, List<Value>>>
@@ -87,6 +89,7 @@ suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
) = set(k, v.toList())
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value>
typealias KeyValuesRepo<Key,Value> = OneToManyKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
keysAndValues: List<Pair<Key, List<Value>>>

View File

@@ -10,6 +10,7 @@ interface ReadStandardCRUDRepo<ObjectType, IdType> : Repo {
suspend fun contains(id: IdType): Boolean
suspend fun count(): Long
}
typealias ReadCRUDRepo<ObjectType, IdType> = ReadStandardCRUDRepo<ObjectType, IdType>
typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType>
val <IdType> UpdatedValuePair<IdType, *>.id
@@ -27,6 +28,7 @@ interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType>
suspend fun deleteById(ids: List<IdType>)
}
typealias WriteCRUDRepo<ObjectType, IdType, InputValueType> = WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.create(
vararg values: InputValueType
@@ -39,4 +41,5 @@ suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectTyp
) = deleteById(ids.toList())
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
typealias CRUDRepo<ObjectType, IdType, InputValueType> = StandardCRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -12,6 +12,7 @@ interface ReadStandardKeyValueRepo<Key, Value> : Repo {
suspend fun contains(key: Key): Boolean
suspend fun count(): Long
}
typealias ReadKeyValueRepo<Key,Value> = ReadStandardKeyValueRepo<Key, Value>
interface WriteStandardKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
@@ -20,6 +21,7 @@ interface WriteStandardKeyValueRepo<Key, Value> : Repo {
suspend fun set(toSet: Map<Key, Value>)
suspend fun unset(toUnset: List<Key>)
}
typealias WriteKeyValueRepo<Key,Value> = WriteStandardKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.set(
vararg toSet: Pair<Key, Value>
@@ -33,4 +35,5 @@ suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unset(
vararg k: Key
) = unset(k.toList())
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value>
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value>
typealias KeyValueRepo<Key,Value> = StandardKeyValueRepo<Key, Value>

View File

@@ -0,0 +1,171 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.common.Warning
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.reverse
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds.*
private inline val String.isAbsolute
get() = startsWith(File.separator)
class FileReadStandardKeyValueRepo(
private val folder: File
) : ReadStandardKeyValueRepo<String, File> {
init {
folder.mkdirs()
}
override suspend fun get(k: String): File? {
val file = File(folder, k)
if (file.exists()) {
return file
}
return null
}
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
return filesPaths.map { File(folder, it) }.createPaginationResult(
resultPagination,
count
)
}
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
return filesPaths.toList().createPaginationResult(
resultPagination,
count
)
}
override suspend fun keys(
v: File,
pagination: Pagination,
reversed: Boolean
): PaginationResult<String> {
val resultPagination = if (reversed) pagination.reverse(1L) else pagination
return if (resultPagination.isFirstPage) {
val fileSubpath = v.absolutePath.removePrefix(folder.absolutePath)
if (fileSubpath == v.absolutePath) {
emptyList()
} else {
listOf(fileSubpath)
}
} else {
emptyList()
}.createPaginationResult(resultPagination, 1L)
}
override suspend fun contains(key: String): Boolean {
return File(folder, key).exists()
}
override suspend fun count(): Long = folder.list() ?.size ?.toLong() ?: 0L
}
/**
* Files watching will not correctly works on Android with version of API lower than API 26
*/
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
class FileWriteStandardKeyValueRepo(
private val folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : WriteStandardKeyValueRepo<String, File> {
private val _onNewValue = MutableSharedFlow<Pair<String, File>>()
override val onNewValue: Flow<Pair<String, File>> = _onNewValue.asSharedFlow()
private val _onValueRemoved = MutableSharedFlow<String>()
override val onValueRemoved: Flow<String> = _onValueRemoved.asSharedFlow()
init {
folder.mkdirs()
filesChangedProcessingScope ?.let {
it.launch {
try {
val folderPath = folder.toPath()
while (isActive) {
val key = try {
folderPath.register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE)
} catch (e: Exception) {
// add verbose way to show that file watching is not working
return@launch
}
for (event in key.pollEvents()) {
val relativeFilePath = (event.context() as? Path) ?: continue
val file = relativeFilePath.toFile()
val relativePath = file.toRelativeString(folder)
when (event.kind()) {
ENTRY_CREATE, ENTRY_MODIFY -> {
launch {
_onNewValue.emit(relativePath to file)
}
}
ENTRY_DELETE -> {
launch {
_onValueRemoved.emit(relativePath)
}
}
}
}
if (key.isValid || folder.exists()) {
continue
}
break
}
} catch (e: Throwable) {
// add verbose way to notify that this functionality is disabled
}
}
}
}
override suspend fun set(toSet: Map<String, File>) {
supervisorScope {
toSet.map { (filename, fileSource) ->
launch {
val file = File(folder, filename)
file.delete()
fileSource.copyTo(file, overwrite = true)
_onNewValue.emit(filename to file)
}
}
}.joinAll()
}
override suspend fun unset(toUnset: List<String>) {
toUnset.forEach {
val file = File(folder, it)
if (file.exists()) {
file.delete()
_onValueRemoved.emit(it)
}
}
}
}
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
class FileStandardKeyValueRepo(
folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : StandardKeyValueRepo<String, File>,
WriteStandardKeyValueRepo<String, File> by FileWriteStandardKeyValueRepo(folder, filesChangedProcessingScope),
ReadStandardKeyValueRepo<String, File> by FileReadStandardKeyValueRepo(folder)