Skip to content

Commit

Permalink
Refactoring configuration parsing (#490)
Browse files Browse the repository at this point in the history
* Refactoring configuration parsing

* Extracting Configuration as separate class

* levelOverridesByInspectionSimpleName becomes part of Configuration too

* Refactoring, moving configuration parsing to Configuration file

* Fixing no dataDir specified

* A unit test enforcing all configuration options are mentioned in README and in compiler plugin help
  • Loading branch information
mccartney authored Mar 2, 2021
1 parent ca1d9c9 commit d022f08
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 182 deletions.
133 changes: 133 additions & 0 deletions src/main/scala/com/sksamuel/scapegoat/Configuration.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.sksamuel.scapegoat

import java.io.File

case class Reports(
disableXML: Boolean,
disableHTML: Boolean,
disableScalastyleXML: Boolean,
disableMarkdown: Boolean
)

case class Configuration(
dataDir: Option[File],
disabledInspections: List[String],
enabledInspections: List[String],
ignoredFiles: List[String],
consoleOutput: Boolean,
verbose: Boolean,
reports: Reports,
customInspectors: Seq[Inspection],
sourcePrefix: String,
minimalLevel: Level,
overrideLevels: Map[String, Level]
)

object Configuration {

def fromPluginOptions(options: List[String]): Configuration = {
def fromProperty[T](propertyName: String, defaultValue: T)(fn: String => T): T = {
options.find(_.startsWith(propertyName + ":")) match {
case Some(property) =>
val justTheValue = property.drop(propertyName.length + 1)
fn(justTheValue)
case None =>
defaultValue
}
}

val disabledInspections =
fromProperty("disabledInspections", defaultValue = List.empty[String])(_.split(':').toList)
val enabledInspections =
fromProperty("enabledInspections", defaultValue = List.empty[String])(_.split(':').toList)
val consoleOutput = fromProperty("consoleOutput", defaultValue = true)(_.toBoolean)
val ignoredFiles =
fromProperty("ignoredFiles", defaultValue = List.empty[String])(_.split(':').toList)
val verbose = fromProperty("verbose", defaultValue = false)(_.toBoolean)

val customInspectors = fromProperty("customInspectors", defaultValue = Seq.empty[Inspection]) {
_.split(':').toSeq
.map(inspection => Class.forName(inspection).getConstructor().newInstance().asInstanceOf[Inspection])
}
val enabledReports = fromProperty("reports", defaultValue = Seq("all"))(_.split(':').toSeq)
val disableXML = !(enabledReports.contains("xml") || enabledReports.contains("all"))
val disableHTML = !(enabledReports.contains("html") || enabledReports.contains("all"))
val disableScalastyleXML =
!(enabledReports.contains("scalastyle") || enabledReports.contains("all"))
val disableMarkdown = !(enabledReports.contains("markdown") || enabledReports.contains("all"))

val levelOverridesByInspectionSimpleName =
fromProperty("overrideLevels", defaultValue = Map.empty[String, Level]) {
_.split(":").map { nameLevel =>
nameLevel.split("=") match {
case Array(insp, level) => insp -> Levels.fromName(level)
case _ =>
throw new IllegalArgumentException(
s"Malformed argument to 'overrideLevels': '$nameLevel'. " +
"Expecting 'name=level' where 'name' is the simple name of " +
"an inspection and 'level' is the simple name of a " +
"com.sksamuel.scapegoat.Level constant, e.g. 'Warning'."
)
}
}.toMap
}
val sourcePrefix = fromProperty("sourcePrefix", defaultValue = "src/main/scala/")(x => x)
val minimalLevel = fromProperty[Level]("minimalLevel", defaultValue = Levels.Info) { value =>
Levels.fromName(value)
}
val dataDir = fromProperty[Option[File]](
"dataDir",
defaultValue = None
) { value =>
Some(new File(value))
}

Configuration(
dataDir = dataDir,
disabledInspections = disabledInspections,
enabledInspections = enabledInspections,
ignoredFiles = ignoredFiles,
consoleOutput = consoleOutput,
verbose = verbose,
reports = Reports(
disableXML = disableXML,
disableHTML = disableHTML,
disableScalastyleXML = disableScalastyleXML,
disableMarkdown = disableMarkdown
),
customInspectors = customInspectors,
sourcePrefix = sourcePrefix,
minimalLevel = minimalLevel,
overrideLevels = levelOverridesByInspectionSimpleName
)
}

val optionsHelp: String = {
Seq(
"-P:scapegoat:dataDir:<pathtodatadir> where the report should be written",
"-P:scapegoat:disabledInspections:<listofinspections> colon separated list of disabled inspections (defaults to none)",
"-P:scapegoat:enabledInspections:<listofinspections> colon separated list of enabled inspections (defaults to all)",
"-P:scapegoat:customInspectors:<listofinspections> colon separated list of custom inspections",
"-P:scapegoat:ignoredFiles:<patterns> colon separated list of regexes to match ",
" files to ignore.",
"-P:scapegoat:verbose:<boolean> enable/disable verbose console messages",
"-P:scapegoat:consoleOutput:<boolean> enable/disable console report output",
"-P:scapegoat:reports:<reports> colon separated list of reports to generate.",
" Valid options are `xml', `html', `scalastyle', 'markdown',",
" or `all'. Use `none' to disable reports.",
"-P:scapegoat:overrideLevels:<levels> override the built in warning levels, e.g. to",
" downgrade a Error to a Warning.",
" <levels> should be a colon separated list of name=level",
" settings, where 'name' is the simple name of an inspection",
" and 'level' is the simple name of a",
" com.sksamuel.scapegoat.Level constant, e.g. 'Warning'.",
" You can use 'all' for inspection name to operate on all inspections.",
"-P:scapegoat:sourcePrefix:<prefix> overrides source prefix if it differs from src/main/scala",
" for ex., in Play applications where sources are in app/ folder",
"-P:scapegoat:minimalLevel:<level> provides minimal level of triggered inspections,",
" that will be shown in a report.",
" 'level' is the simple name of a",
" com.sksamuel.scapegoat.Level constant, e.g. 'Warning'."
).mkString("\n")
}
}
17 changes: 8 additions & 9 deletions src/main/scala/com/sksamuel/scapegoat/Feedback.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ import scala.tools.nsc.reporters.Reporter
* @author Stephen Samuel
*/
class Feedback(
consoleOutput: Boolean,
reporter: Reporter,
sourcePrefix: String,
minimalLevel: Level = Levels.Info
configuration: Configuration
) {

private val warningsBuffer = new ListBuffer[Warning]

def warnings: Seq[Warning] = warningsBuffer.toSeq
def warningsWithMinimalLevel: Seq[Warning] = warnings.filter(_.hasMinimalLevelOf(minimalLevel))
def shouldPrint(warning: Warning): Boolean = consoleOutput && warning.hasMinimalLevelOf(minimalLevel)

var levelOverridesByInspectionSimpleName: Map[String, Level] = Map.empty
def warningsWithMinimalLevel: Seq[Warning] =
warnings.filter(_.hasMinimalLevelOf(configuration.minimalLevel))
def shouldPrint(warning: Warning): Boolean =
configuration.consoleOutput && warning.hasMinimalLevelOf(configuration.minimalLevel)

def infos = warnings(Levels.Info)
def errors = warnings(Levels.Error)
Expand All @@ -38,8 +36,8 @@ class Feedback(
val name = inspection.name
val explanation = adhocExplanation.getOrElse(inspection.explanation)
val adjustedLevel = (
levelOverridesByInspectionSimpleName.get("all"),
levelOverridesByInspectionSimpleName.get(inspection.getClass.getSimpleName)
configuration.overrideLevels.get("all"),
configuration.overrideLevels.get(inspection.getClass.getSimpleName)
) match {
case (Some(l), _) => l
case (None, Some(l)) => l
Expand Down Expand Up @@ -74,6 +72,7 @@ class Feedback(
}

private def normalizeSourceFile(sourceFile: String): String = {
val sourcePrefix = configuration.sourcePrefix
val indexOf = sourceFile.indexOf(sourcePrefix)
val fullPrefix = if (sourcePrefix.endsWith("/")) sourcePrefix else s"$sourcePrefix/"
val packageAndFile = if (indexOf == -1) sourceFile else sourceFile.drop(indexOf).drop(fullPrefix.length)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.sksamuel.scapegoat.inspections.unsafe._
/**
* @author Stephen Samuel
*/
object ScapegoatConfig extends App {
object Inspections extends App {

def inspections: Seq[Inspection] =
Seq(
Expand Down
Loading

0 comments on commit d022f08

Please sign in to comment.