main.ch.difty.kris.KRisExtensions.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kris-core Show documentation
Show all versions of kris-core Show documentation
Kotlin library for importing/exporting bibliographic records in RIS format
@file:Suppress("SpellCheckingInspection")
@file:OptIn(ExperimentalCoroutinesApi::class)
package ch.difty.kris
import ch.difty.kris.domain.RisRecord
import ch.difty.kris.domain.RisTag
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.produceIn
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import java.nio.channels.ClosedChannelException
//region:import - RISFile lines -> RisRecords
/**
* Converts a flow of Strings (representing lines in a RIS file) (as receiver) into a flow of [RisRecord]s.
* May throw a [KRisException] if the line flow cannot be parsed successfully.
*/
public fun Flow.toRisRecords(): Flow = KRis.process(this)
/**
* Converts a list of Strings (representing lines in a RIS file) (as receiver) itno a list of [RisRecord]s.
* May throw a [KRisException] if the line list of Strings cannot be parsed successfully.
*/
public fun List.toRisRecords(): List = KRis.processList(this)
/**
* Converts a sequence of Strings (representing lines in a RIS file) (as receiver) into a sequence of [RisRecord]s
* in a blocking manner. May throw a [KRisException] if the line flow cannot be parsed successfully.
*/
@OptIn(DelicateCoroutinesApi::class)
public fun Sequence.toRisRecords(scope: CoroutineScope = GlobalScope): Sequence =
mapSequence(KRis::process, scope)
//endregion
//region:export - RisRecords -> RISFile lines
/**
* Converts a flow of [RisRecord]s into a flow of [String]s in RIS file format.
* Optionally accepts a list of names of RisTags defining a sort order for the RisTags in the file.
*/
@JvmOverloads
public fun Flow.toRisLines(sort: List = emptyList()): Flow = KRis.build(this, sort)
/**
* Converts a list of [RisRecord]s into a list of [String]s in RIS file format.
* Optionally accepts a list of names of RisTags defining a sort order for the RisTags in the file.
*/
@JvmOverloads
public fun List.toRisLines(sort: List = emptyList()): List =
runBlocking { asFlow().toRisLines(sort).toList() }
/**
* Processes a sequence of [RisRecord]s into a sequence of Strings representing lines in RIS file format.
*/
@OptIn(DelicateCoroutinesApi::class)
public fun Sequence.toRisLines(scope: CoroutineScope = GlobalScope): Sequence =
mapSequence(KRis::build, scope)
//endregion
/**
* Processes a sequence of type [T] into a sequence of type [R] using a [flowMapper]
* accepting a flow of type [T] and returning a flow of type [R].
* Thanks to @jcornaz for the help.
*/
@OptIn(FlowPreview::class)
@Suppress("SwallowedException")
private fun Sequence.mapSequence(
flowMapper: (Flow) -> Flow,
scope: CoroutineScope,
): Sequence = sequence {
val sourceFlow: Flow = [email protected]()
val targetFlow: Flow = flowMapper(sourceFlow)
val channel: ReceiveChannel = targetFlow.produceIn(scope)
try {
while (!channel.isClosedForReceive) {
yield(runBlocking { channel.receive() })
}
} catch (closed: ClosedReceiveChannelException) {
// flow is completed -> swallow
} catch (closed: ClosedChannelException) {
// flow is completed -> swallow
} finally {
channel.cancel()
}
}
/**
* List of the names of all [RisTag]s.
*/
public val risTagNames: List get() = RisTag.values().map { it.name }