All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.ch.difty.kris.KRisExtensions.kt Maven / Gradle / Ivy

There is a newer version: 0.5.3
Show newest version
@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 }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy