Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supported multiple reporters in diktat runner #1803

Merged
merged 17 commits into from
Nov 16, 2023
Merged
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ out/
*.sarif

# a generated file for github action
/gradle/libs.versions.toml_snapshot
/gradle/libs.versions.toml_backup
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import org.jetbrains.kotlin.incremental.createDirectory
import java.nio.file.Files
import java.nio.file.StandardCopyOption

@Suppress("DSL_SCOPE_VIOLATION", "RUN_IN_SCRIPT") // https://github.com/gradle/gradle/issues/22797
plugins {
Expand Down Expand Up @@ -60,7 +61,7 @@ tasks.create("generateLibsForDiktatSnapshot") {
.let {
val libsFileForDiktatSnapshot = dir.resolve(libsFileName)
Files.write(libsFileForDiktatSnapshot.toPath(), it)
Files.move(libsFile.toPath(), libsFileBackup.toPath())
Files.move(libsFile.toPath(), libsFileBackup.toPath(), StandardCopyOption.REPLACE_EXISTING)
Files.copy(libsFileForDiktatSnapshot.toPath(), libsFile.toPath())
}

Expand Down
13 changes: 3 additions & 10 deletions diktat-api/src/main/kotlin/com/saveourtool/diktat/DiktatRunner.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.saveourtool.diktat

import com.saveourtool.diktat.api.DiktatBaseline
import com.saveourtool.diktat.api.DiktatBaseline.Companion.skipKnownErrors
import com.saveourtool.diktat.api.DiktatProcessorListener
import com.saveourtool.diktat.api.DiktatProcessorListener.Companion.countErrorsAsProcessorListener
import com.saveourtool.diktat.api.DiktatReporter
Expand All @@ -15,15 +13,11 @@ private typealias RunAction = (DiktatProcessor, DiktatProcessorListener) -> Unit
/**
* A runner for diktat on bunch of files using baseline and reporter
*
* @param diktatBaselineGenerator
* @property diktatProcessor
* @property diktatBaseline
* @param diktatProcessor
* @property diktatReporter
*/
data class DiktatRunner(
val diktatProcessor: DiktatProcessor,
val diktatBaseline: DiktatBaseline,
private val diktatBaselineGenerator: DiktatProcessorListener,
private val diktatProcessor: DiktatProcessor,
val diktatReporter: DiktatReporter,
) {
private fun doRun(
Expand All @@ -35,8 +29,7 @@ data class DiktatRunner(
diktatProcessor,
DiktatProcessorListener(
args.loggingListener,
diktatReporter.skipKnownErrors(diktatBaseline),
diktatBaselineGenerator,
diktatReporter,
errorCounter.countErrorsAsProcessorListener()
),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.saveourtool.diktat

import com.saveourtool.diktat.api.DiktatProcessorListener
import com.saveourtool.diktat.api.DiktatReporterCreationArguments
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Path
import kotlin.io.path.inputStream

Expand All @@ -13,42 +13,30 @@ import kotlin.io.path.inputStream
* @property sourceRootDir a common root dir for all provided [files]
* @property files a collection of files which needs to be fixed
* @property baselineFile an optional path to file with baseline
* @property reporterType type of reporter to report the detected errors
* @property reporterOutput output for reporter
* @property groupByFileInPlain a flag `groupByFile` which is applicable for plain reporter only, **null** by default
* @property colorNameInPlain a color name which is applicable for plain reporter only, **null** by default
* @property reporterArgsList list of arguments to create reporters to report result
* @property loggingListener listener to log diktat runner phases, [DiktatProcessorListener.empty] by default
*/
data class DiktatRunnerArguments(
val configInputStream: InputStream,
val sourceRootDir: Path?,
val files: Collection<Path>,
val baselineFile: Path?,
val reporterType: String,
val reporterOutput: OutputStream?,
val groupByFileInPlain: Boolean? = null,
val colorNameInPlain: String? = null,
val reporterArgsList: List<DiktatReporterCreationArguments> = emptyList(),
val loggingListener: DiktatProcessorListener = DiktatProcessorListener.empty,
) {
constructor(
configFile: Path,
sourceRootDir: Path?,
files: Collection<Path>,
baselineFile: Path?,
reporterType: String,
reporterOutput: OutputStream?,
groupByFileInPlain: Boolean? = null,
colorNameInPlain: String? = null,
reporterArgsList: List<DiktatReporterCreationArguments> = emptyList(),
loggingListener: DiktatProcessorListener = DiktatProcessorListener.empty,
) : this(
configFile.inputStream(),
sourceRootDir,
files,
baselineFile,
reporterType,
reporterOutput,
groupByFileInPlain,
colorNameInPlain,
reporterArgsList,
loggingListener,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.saveourtool.diktat

import com.saveourtool.diktat.api.DiktatBaseline
import com.saveourtool.diktat.api.DiktatBaseline.Companion.skipKnownErrors
import com.saveourtool.diktat.api.DiktatBaselineFactory
import com.saveourtool.diktat.api.DiktatProcessorListener
import com.saveourtool.diktat.api.DiktatReporter
Expand All @@ -9,7 +10,6 @@ import com.saveourtool.diktat.api.DiktatRuleConfig
import com.saveourtool.diktat.api.DiktatRuleConfigReader
import com.saveourtool.diktat.api.DiktatRuleSet
import com.saveourtool.diktat.api.DiktatRuleSetFactory
import java.io.OutputStream
import java.nio.file.Path

/**
Expand Down Expand Up @@ -37,16 +37,14 @@ class DiktatRunnerFactory(
val diktatRuleSet = diktatRuleSetFactory(diktatRuleConfigs)
val processor = diktatProcessorFactory(diktatRuleSet)
val (baseline, baselineGenerator) = resolveBaseline(args.baselineFile, args.sourceRootDir)
val reporter = resolveReporter(
args.reporterType, args.reporterOutput,
args.colorNameInPlain, args.groupByFileInPlain,
args.sourceRootDir
)

val reporter = args.reporterArgsList
.map { diktatReporterFactory(it) }
.let { DiktatReporter.union(it) }

return DiktatRunner(
diktatProcessor = processor,
diktatBaseline = baseline,
diktatBaselineGenerator = baselineGenerator,
diktatReporter = reporter,
diktatReporter = DiktatReporter(reporter.skipKnownErrors(baseline), baselineGenerator),
)
}

Expand All @@ -62,25 +60,4 @@ class DiktatRunnerFactory(
} ?: DiktatProcessorListener.empty
DiktatBaseline.empty to baselineGenerator
}

private fun resolveReporter(
reporterType: String,
reporterOutput: OutputStream?,
colorNameInPlain: String?,
groupByFileInPlain: Boolean?,
sourceRootDir: Path?,
): DiktatReporter {
val (outputStream, closeOutputStream) = reporterOutput?.let { it to true } ?: (System.`out` to false)
return if (reporterType == diktatReporterFactory.plainId) {
diktatReporterFactory.createPlain(outputStream, closeOutputStream, sourceRootDir, colorNameInPlain, groupByFileInPlain)
} else {
require(colorNameInPlain == null) {
"colorization is applicable only for plain reporter"
}
require(groupByFileInPlain == null) {
"groupByFile is applicable only for plain reporter"
}
diktatReporterFactory(reporterType, outputStream, closeOutputStream, sourceRootDir)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.saveourtool.diktat.api

import com.saveourtool.diktat.util.DiktatProcessorListenerWrapper
import java.nio.file.Path

/**
Expand All @@ -22,21 +23,24 @@ fun interface DiktatBaseline {
* @param baseline
* @return wrapped [DiktatProcessorListener] which skips known errors based on [baseline]
*/
fun DiktatProcessorListener.skipKnownErrors(baseline: DiktatBaseline): DiktatProcessorListener = object : DiktatProcessorListener {
override fun onError(
fun DiktatProcessorListener.skipKnownErrors(baseline: DiktatBaseline): DiktatProcessorListener = object : DiktatProcessorListenerWrapper<DiktatProcessorListener>(
this@skipKnownErrors
) {
override fun doOnError(
wrappedValue: DiktatProcessorListener,
file: Path,
error: DiktatError,
isCorrected: Boolean
) {
if (!baseline.errorsByFile(file).contains(error)) {
this@skipKnownErrors.onError(file, error, isCorrected)
wrappedValue.onError(file, error, isCorrected)
}
}

override fun beforeAll(files: Collection<Path>) = this@skipKnownErrors.beforeAll(files)
override fun before(file: Path) = this@skipKnownErrors.before(file)
override fun after(file: Path) = this@skipKnownErrors.after(file)
override fun afterAll() = this@skipKnownErrors.afterAll()
override fun doBeforeAll(wrappedValue: DiktatProcessorListener, files: Collection<Path>) = wrappedValue.beforeAll(files)
override fun doBefore(wrappedValue: DiktatProcessorListener, file: Path) = wrappedValue.before(file)
override fun doAfter(wrappedValue: DiktatProcessorListener, file: Path) = wrappedValue.after(file)
override fun doAfterAll(wrappedValue: DiktatProcessorListener) = wrappedValue.afterAll()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.saveourtool.diktat.api

import com.saveourtool.diktat.util.DiktatProcessorListenerWrapper
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger

private typealias DiktatProcessorListenerIterable = Iterable<DiktatProcessorListener>

/**
* A listener for [com.saveourtool.diktat.DiktatProcessor]
*/
Expand Down Expand Up @@ -57,18 +60,25 @@ interface DiktatProcessorListener {
* @param listeners
* @return a single [DiktatProcessorListener] which uses all provided [listeners]
*/
operator fun invoke(vararg listeners: DiktatProcessorListener): DiktatProcessorListener = object : DiktatProcessorListener {
override fun beforeAll(files: Collection<Path>) = listeners.forEach { it.beforeAll(files) }
override fun before(file: Path) = listeners.forEach { it.before(file) }
override fun onError(
fun union(listeners: DiktatProcessorListenerIterable): DiktatProcessorListener = object : DiktatProcessorListenerWrapper<DiktatProcessorListenerIterable>(listeners) {
override fun doBeforeAll(wrappedValue: DiktatProcessorListenerIterable, files: Collection<Path>) = wrappedValue.forEach { it.beforeAll(files) }
override fun doBefore(wrappedValue: DiktatProcessorListenerIterable, file: Path) = wrappedValue.forEach { it.before(file) }
override fun doOnError(
wrappedValue: DiktatProcessorListenerIterable,
file: Path,
error: DiktatError,
isCorrected: Boolean
) = listeners.forEach { it.onError(file, error, isCorrected) }
override fun after(file: Path) = listeners.forEach { it.after(file) }
override fun afterAll() = listeners.forEach(DiktatProcessorListener::afterAll)
) = wrappedValue.forEach { it.onError(file, error, isCorrected) }
override fun doAfter(wrappedValue: DiktatProcessorListenerIterable, file: Path) = wrappedValue.forEach { it.after(file) }
override fun doAfterAll(wrappedValue: DiktatProcessorListenerIterable) = wrappedValue.forEach(DiktatProcessorListener::afterAll)
}

/**
* @param listeners
* @return a single [DiktatProcessorListener] which uses all provided [listeners]
*/
operator fun invoke(vararg listeners: DiktatProcessorListener): DiktatProcessorListener = union(listeners.asIterable())

/**
* @return An implementation of [DiktatProcessorListener] which counts [DiktatError]s
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Contains a base interface and implementations for a container with arguments to create a reporter
*/

package com.saveourtool.diktat.api

import java.io.OutputStream
import java.nio.file.Path

/**
* Arguments to create [DiktatReporter] using [DiktatReporterFactory]
*/
sealed interface DiktatReporterCreationArguments {
/**
* Identifier of [DiktatReporter] which needs to be created
*/
val id: String

/**
* Output for [DiktatReporter]
*/
val outputStream: OutputStream

/**
* Should [outputStream] be closed af the end of [DiktatReporter]
*/
val closeOutputStreamAfterAll: Boolean

/**
* Directory to base source root to report relative paths in [DiktatReporter]
*/
val sourceRootDir: Path?

companion object {
/**
* @param id ID of [DiktatReporter]
* @param outputStream stdout will be used when it's empty
* @param sourceRootDir a dir to detect relative path for processing files
* @param colorNameInPlain a color name for colorful output which is applicable for plain ([DiktatReporterFactory.PLAIN_ID]) reporter only,
* `null` means to disable colorization.
* @param groupByFileInPlain a flag `groupByFile` which is applicable for plain ([DiktatReporterFactory.PLAIN_ID]) reporter only.
* @return created [DiktatReporter]
*/
operator fun invoke(
id: String,
outputStream: OutputStream?,
sourceRootDir: Path?,
colorNameInPlain: String? = null,
groupByFileInPlain: Boolean? = null,
): DiktatReporterCreationArguments {
val (outputStreamOrStdout, closeOutputStreamAfterAll) = outputStream?.let { it to true } ?: (System.`out` to false)
return if (id == DiktatReporterFactory.PLAIN_ID) {
PlainDiktatReporterCreationArguments(
outputStreamOrStdout, closeOutputStreamAfterAll, sourceRootDir, colorNameInPlain, groupByFileInPlain
)
} else {
require(colorNameInPlain == null) {
"colorization is applicable only for plain reporter"
}
require(groupByFileInPlain == null) {
"groupByFile is applicable only for plain reporter"
}
DiktatReporterCreationArgumentsImpl(
id, outputStreamOrStdout, closeOutputStreamAfterAll, sourceRootDir
)
}
}
}
}

/**
* Implementation of [DiktatReporterCreationArguments] for [DiktatReporterFactory.PLAIN_ID]
*
* @property outputStream
* @property closeOutputStreamAfterAll
* @property sourceRootDir
* @property colorName name of color for colorful output, `null` means to disable colorization.
* @property groupByFile
*/
data class PlainDiktatReporterCreationArguments(
override val outputStream: OutputStream,
override val closeOutputStreamAfterAll: Boolean,
override val sourceRootDir: Path?,
val colorName: String? = null,
val groupByFile: Boolean? = null,
) : DiktatReporterCreationArguments {
override val id: String = DiktatReporterFactory.PLAIN_ID
}

/**
* @property id
* @property outputStream
* @property closeOutputStreamAfterAll
* @property sourceRootDir
*/
private data class DiktatReporterCreationArgumentsImpl(
override val id: String,
override val outputStream: OutputStream,
override val closeOutputStreamAfterAll: Boolean,
override val sourceRootDir: Path?,
) : DiktatReporterCreationArguments
Loading
Loading