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

commonMain.pipeline.ProcessorPipeline.kt Maven / Gradle / Ivy

There is a newer version: 2.16.0
Show newest version
/*
 * Copyright 2019-2022 Mamoe Technologies and contributors.
 *
 * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
 * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
 *
 * https://github.com/mamoe/mirai/blob/dev/LICENSE
 */

package net.mamoe.mirai.internal.pipeline

import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext
import net.mamoe.mirai.internal.network.components.NoticeProcessor
import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable
import net.mamoe.mirai.utils.*
import java.io.Closeable
import java.util.*
import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentLinkedQueue

internal interface Processor, D> : PipelineConsumptionMarker {
    val origin: Any get() = this

    suspend fun process(context: C, data: D)
}

internal interface ProcessorPipeline

, C : ProcessorPipelineContext, D, R> { val processors: MutableCollection> fun interface DisposableRegistry : Closeable { fun dispose() override fun close() { dispose() } } fun registerProcessor(processor: P): DisposableRegistry // after fun registerBefore(processor: P): DisposableRegistry fun createContext(data: D, attributes: TypeSafeMap): C /** * Process using the [context]. */ suspend fun process( data: D, context: C, attributes: TypeSafeMap = TypeSafeMap.EMPTY, ): ProcessResult /** * Process with a new context */ suspend fun process( data: D, attributes: TypeSafeMap = TypeSafeMap.EMPTY ): ProcessResult } internal inline fun

, Pip : ProcessorPipeline> Pip.replaceProcessor( predicate: (origin: Any) -> Boolean, processor: P ): Boolean { for (box in processors) { val value = box.value if (predicate(value.origin)) { box.value = processor return true } } return false } internal data class ProcessorBox

>( var value: P ) internal data class ProcessResult, R>( val context: C, val collected: Collection, ) @JvmInline internal value class MutablePipelineResult( val data: MutableCollection ) internal interface PipelineConsumptionMarker internal interface ProcessorPipelineContext { /** * Child processes ([processAlso]) will inherit [attributes] from its parent, while any other properties from the context will not. */ val attributes: TypeSafeMap val collected: MutablePipelineResult // DSL to simplify some expressions operator fun MutablePipelineResult.plusAssign(result: R?) { if (result != null) collect(result) } /** * Collect a result. */ fun collect(result: R) /** * Collect results. */ fun collect(results: Iterable) val isConsumed: Boolean /** * Marks the input as consumed so that there will not be warnings like 'Unknown type xxx'. This will not stop the pipeline. * * If this is executed, make sure you provided all information important for debugging. * * You need to invoke [markAsConsumed] if your implementation includes some `else` branch which covers all situations, * and throws a [contextualBugReportException] or logs something. */ @ConsumptionMarker fun PipelineConsumptionMarker.markAsConsumed(marker: Any = this) /** * Marks the input as not consumed, if it was marked by this [NoticeProcessor]. */ @ConsumptionMarker fun PipelineConsumptionMarker.markNotConsumed(marker: Any = this) @DslMarker annotation class ConsumptionMarker // to give an explicit color. /** * Fire the [data] into the processor pipeline, and collect the results to current [collected], updating *some mutable properties* in contexts, e.g. [OutgoingMessagePipelineContext.currentMessageChain] * * @param extraAttributes extra attributes * @return result collected from processors. This would also have been collected to this context (where you call [processAlso]). */ suspend fun processAlso( data: D, extraAttributes: TypeSafeMap = TypeSafeMap.EMPTY ): ProcessResult, R> } internal abstract class AbstractProcessorPipelineContext( override val attributes: TypeSafeMap, private val traceLogging: MiraiLogger, ) : ProcessorPipelineContext { private val consumers: Stack = Stack() override val isConsumed: Boolean get() = consumers.isNotEmpty() override fun PipelineConsumptionMarker.markAsConsumed(marker: Any) { traceLogging.info { "markAsConsumed: marker=$marker" } consumers.push(marker) } override fun PipelineConsumptionMarker.markNotConsumed(marker: Any) { if (consumers.peek() === marker) { consumers.pop() traceLogging.info { "markNotConsumed: Y, marker=$marker" } } else { traceLogging.info { "markNotConsumed: N, marker=$marker" } } } override val collected: MutablePipelineResult = MutablePipelineResult(ConcurrentLinkedQueue()) override fun collect(result: R) { collected.data.add(result) traceLogging.info { "collect: $result" } } override fun collect(results: Iterable) { this.collected.data.addAll(results) traceLogging.info { "collect: $results" } } } internal class PipelineConfiguration( var stopWhenConsumed: Boolean ) internal abstract class AbstractProcessorPipeline

, C : ProcessorPipelineContext, D, R> protected constructor( val configuration: PipelineConfiguration, val traceLogging: MiraiLogger, ) : ProcessorPipeline { constructor(configuration: PipelineConfiguration) : this(configuration, SilentLogger) /** * Must be ordered */ override val processors: ConcurrentLinkedDeque> = ConcurrentLinkedDeque() override fun registerProcessor(processor: P): ProcessorPipeline.DisposableRegistry { val box = ProcessorBox(processor) processors.add(box) return ProcessorPipeline.DisposableRegistry { processors.remove(box) } } override fun registerBefore(processor: P): ProcessorPipeline.DisposableRegistry { val box = ProcessorBox(processor) processors.addFirst(box) return ProcessorPipeline.DisposableRegistry { processors.remove(box) } } abstract inner class BaseContextImpl( attributes: TypeSafeMap, ) : AbstractProcessorPipelineContext(attributes, traceLogging) { override suspend fun processAlso( data: D, extraAttributes: TypeSafeMap ): ProcessResult, R> { traceLogging.info { "processAlso: data=${data.structureToStringAndDesensitizeIfAvailable()}" } traceLogging.info { "extraAttributes = $extraAttributes" } val newAttributes = this.attributes + extraAttributes traceLogging.info { "newAttributes = $newAttributes" } return process(data, newAttributes).also { this.collected.data += it.collected traceLogging.info { "processAlso: result=$it" } } } } protected open fun handleExceptionInProcess( data: D, context: C, attributes: TypeSafeMap, processor: P, e: Throwable ): Unit = throw e override suspend fun process(data: D, attributes: TypeSafeMap): ProcessResult { return process(data, createContext(data, attributes), attributes) } override suspend fun process(data: D, context: C, attributes: TypeSafeMap): ProcessResult { traceLogging.info { "process: data=${data.structureToStringAndDesensitizeIfAvailable()}" } val diff = if (traceLogging.isEnabled) CollectionDiff() else null diff?.save(context.collected.data) for ((processor) in processors) { val result = kotlin.runCatching { processor.process(context, data) }.onFailure { e -> handleExceptionInProcess(data, context, attributes, processor, e) } diff?.run { val diffPackets = subtractAndSave(context.collected.data) traceLogging.info { "Finished ${ processor.toString().replace("net.mamoe.mirai.internal.network.notice.", "") }, success=${result.isSuccess}, consumed=${context.isConsumed}, diff=$diffPackets" } } if (context.isConsumed && configuration.stopWhenConsumed) { traceLogging.info { "stopWhenConsumed=true, stopped." } break } } return ProcessResult(context, context.collected.data) } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy