com.ossuminc.riddl.command.CommonOptionsHelper.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of riddl-commands_3 Show documentation
Show all versions of riddl-commands_3 Show documentation
RIDDL Command Infrastructure and command definitions
The newest version!
/*
* Copyright 2019 Ossum, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
package com.ossuminc.riddl.command
import com.ossuminc.riddl.language.Messages.*
import com.ossuminc.riddl.language.Messages
import com.ossuminc.riddl.utils.{CommonOptions, pc}
import com.ossuminc.riddl.utils.StringHelpers
import com.ossuminc.riddl.utils.{PlatformContext, RiddlBuildInfo, SysLogger}
import org.ekrich.config.*
import scopt.*
import java.nio.file.Path
import java.time.{Clock, ZoneId}
import java.util.concurrent.TimeUnit
import scala.concurrent.duration.FiniteDuration
import scala.util.control.NonFatal
/** Handle processing of Language module's CommonOptions */
object CommonOptionsHelper:
private def year: Int = Clock.systemUTC().instant().atZone(ZoneId.systemDefault()).getYear
private val start: String = RiddlBuildInfo.startYear
val blurb: String =
s"""RIDDL Compiler © $start-$year Ossum Inc. All rights reserved."
|Version: ${RiddlBuildInfo.version}
|
|This program parses, validates and translates RIDDL sources to other kinds
|of documents. RIDDL is a language for system specification based on Domain
|Drive Design, Reactive Architecture, and distributed system principles.
|
|""".stripMargin
private inline def show_times = "show-times"
private inline def show_include_times = "show-include-times"
private inline def dry_run = "dry-run"
private inline def verbose = "verbose"
private inline def debug = "debug"
private inline def quiet = "quiet"
private inline def no_ansi_messages = "no-ansi-messages"
private inline def show_warnings = "show-warnings"
private inline def show_missing_warnings = "show-missing-warnings"
private inline def show_style_warnings = "show-style-warnings"
private inline def show_usage_warnings = "show-usage-warnings"
private inline def show_info_messages = "show-info-messages"
private inline def sort_messages_by_location = "sort-messages-by-location"
private inline def group_messages_by_kind = "group-messages-by-kind"
private inline def max_parallel_parsing = "max-parallel-parsing"
private inline def max_include_wait = "max-include-wait"
private inline def warnings_are_fatal = "warnings-are-fatal"
lazy val commonOptionsParser: OParser[Unit, CommonOptions] = {
val builder: OParserBuilder[CommonOptions] = OParser.builder[CommonOptions]
import builder.*
OParser.sequence(
programName("riddlc"),
head(blurb),
opt[Unit]('t', name = show_times)
.optional()
.action((_, c) => c.copy(showTimes = true))
.text("Show parsing phase execution times"),
opt[Unit]('I', name = show_include_times)
.optional()
.action((_, c) => c.copy(showIncludeTimes = true))
.text("Show parsing of included files execution times"),
opt[Unit]('d', dry_run)
.optional()
.action((_, c) => c.copy(dryRun = true))
.text("go through the motions but don't write any changes"),
opt[Unit]('v', verbose)
.optional()
.action((_, c) => c.copy(verbose = true))
.text("Provide verbose output detailing actions taken by riddlc"),
opt[Unit]('D', debug)
.optional()
.action((_, c) => c.copy(debug = true))
.text("Enable debug output. Only useful for riddlc developers"),
opt[Unit]('q', quiet)
.optional()
.action((_, c) => c.copy(quiet = true))
.text("Do not print out any output, just do the requested command"),
opt[Unit]('a', no_ansi_messages)
.optional()
.action((_, c) => c.copy(noANSIMessages = true))
.text("Do not print messages with ANSI formatting"),
opt[Boolean]('w', name = show_warnings)
.optional()
.action((s, c) =>
c.copy(
showWarnings = s,
showMissingWarnings = s,
showStyleWarnings = s,
showUsageWarnings = s
)
)
.text("Suppress all warning messages so only errors are shown"),
opt[Boolean]('m', name = show_missing_warnings)
.optional()
.action((s, c) => c.copy(showMissingWarnings = s))
.text("Suppress warnings about things that are missing"),
opt[Boolean]('s', name = show_style_warnings)
.optional()
.action((s, c) => c.copy(showStyleWarnings = s))
.text("Suppress warnings about questionable input style. "),
opt[Boolean]('u', name = show_usage_warnings)
.optional()
.action((s, c) => c.copy(showUsageWarnings = s))
.text("Suppress warnings about usage of definitions. "),
opt[Boolean]('i', name = show_info_messages)
.optional()
.action((s, c) => c.copy(showInfoMessages = s))
.text("Suppress information output"),
opt[Boolean]('S', name = sort_messages_by_location)
.optional()
.action((_, c) => c.copy(sortMessagesByLocation = true))
.text(
"Print all messages sorted by the file name and line number in which they occur."
),
opt[Boolean]('G', name = group_messages_by_kind)
.optional()
.action((_, c) => c.copy(groupMessagesByKind = true))
.text(
"Print all messages by their severity kind"
),
opt[Int]('x', name = max_parallel_parsing)
.optional()
.action((v, c) => c.copy(maxParallelParsing = v))
.text(
"Controls the maximum number of include files that will be parsed in parallel"
),
opt[Int](name = max_include_wait)
.optional()
.action((v, c) => c.copy(maxIncludeWait = FiniteDuration(v, "seconds")))
.text("Maximum time that parsing an include file will wait for it to complete"),
opt[Boolean](warnings_are_fatal)
.optional()
.action((_, c) => c.copy(warningsAreFatal = true))
.text(
"Makes validation warnings fatal to encourage code perfection"
)
)
}
def parseCommonOptions(
args: Array[String]
)(using io: PlatformContext): (Option[CommonOptions], Array[String]) = {
val setup: OParserSetup = new DefaultOParserSetup {
override def showUsageOnError: Option[Boolean] = Option(false)
override def renderingMode: RenderingMode.TwoColumns.type =
RenderingMode.TwoColumns
}
val dontTerminate: DefaultOEffectSetup = new DefaultOEffectSetup {
override def displayToOut(msg: String): Unit = { io.log.info(msg) }
override def displayToErr(msg: String): Unit = { io.log.error(msg) }
override def reportError(msg: String): Unit = { io.log.error(msg) }
override def reportWarning(msg: String): Unit = { io.log.warn(msg) }
// ignore terminate
override def terminate(exitState: Either[String, Unit]): Unit = ()
}
val saneArgs = args.map(_.trim).filter(_.nonEmpty)
val options = saneArgs.takeWhile(_.startsWith("-"))
val remainingOptions = saneArgs.dropWhile(_.startsWith("-"))
val (common, effects1) = OParser.runParser[CommonOptions](
commonOptionsParser,
options,
com.ossuminc.riddl.utils.CommonOptions.default,
setup
)
OParser.runEffects(effects1, dontTerminate)
common -> remainingOptions
}
private def noBool = Option.empty[Boolean]
private def commonOptionsReader(config: Config): CommonOptions =
val default = CommonOptions()
val obj = config.getObject("common").toConfig
val showTimes = if obj.hasPath(show_times) then obj.getBoolean(show_times) else default.showTimes
val showIncludeTimes =
if obj.hasPath(show_include_times) then obj.getBoolean(show_include_times) else default.showIncludeTimes
val verboseV = if obj.hasPath(verbose) then obj.getBoolean(verbose) else default.verbose
val dryRunV = if obj.hasPath(dry_run) then obj.getBoolean(dry_run) else default.dryRun
val quietV = if obj.hasPath(quiet) then obj.getBoolean(quiet) else default.quiet
val debugV = if obj.hasPath(debug) then obj.getBoolean(debug) else default.debug
val noANSIMessages =
if obj.hasPath(no_ansi_messages) then obj.getBoolean(no_ansi_messages) else default.noANSIMessages
val sortMessagesByLocation =
if obj.hasPath(sort_messages_by_location) then
obj.getBoolean(sort_messages_by_location)
else default.sortMessagesByLocation
val groupMessagesByKind =
if obj.hasPath(group_messages_by_kind) then
obj.getBoolean(group_messages_by_kind)
else
default.groupMessagesByKind
val showWarnings = if obj.hasPath(show_warnings) then obj.getBoolean(show_warnings) else default.showWarnings
val showStyleWarnings =
if obj.hasPath(show_style_warnings) then obj.getBoolean(show_style_warnings) else default.showStyleWarnings
val showMissingWarnings =
if obj.hasPath(show_missing_warnings) then obj.getBoolean(show_missing_warnings) else default.showMissingWarnings
val showUsageWarnings =
if obj.hasPath(show_usage_warnings) then obj.getBoolean(show_usage_warnings) else default.showUsageWarnings
val showInfoMessages =
if obj.hasPath(show_info_messages) then obj.getBoolean(show_info_messages) else default.showInfoMessages
val maxParallelParsing =
if obj.hasPath(max_parallel_parsing) then obj.getInt(max_parallel_parsing) else default.maxParallelParsing
val maxIncludeWait =
if obj.hasPath(max_include_wait) then
val duration = obj.getDuration(max_include_wait)
val seconds = duration.toMillis
FiniteDuration(seconds,TimeUnit.MILLISECONDS)
else default.maxIncludeWait
val warningsAreFatal =
if obj.hasPath(warnings_are_fatal) then obj.getBoolean(warnings_are_fatal) else default.warningsAreFatal
CommonOptions(
showTimes,
showIncludeTimes,
verboseV,
dryRunV,
quietV,
showWarnings,
showMissingWarnings,
showStyleWarnings,
showUsageWarnings,
showInfoMessages,
debugV,
sortMessagesByLocation,
groupMessagesByKind,
noANSIMessages,
maxParallelParsing,
maxIncludeWait,
warningsAreFatal
)
end commonOptionsReader
def loadCommonOptions(configFile: Path)(using pc: PlatformContext): Either[Messages, CommonOptions] =
val options: ConfigParseOptions = ConfigParseOptions.defaults
.setAllowMissing(true)
.setOriginDescription(configFile.getFileName.toString)
try {
val config = ConfigFactory.parseFile(configFile.toFile, options)
if pc.options.verbose then {
pc.log.info(s"Read command options from $configFile")
}
val opt = commonOptionsReader(config)
if pc.options.debug then {
println(StringHelpers.toPrettyString(options, 1, Some("Loaded common options:")))
}
Right(opt)
} catch {
case NonFatal(xcptn) =>
Left(
errors(s"Failed to load options from $configFile because:\n" +
xcptn.getClass.getSimpleName + ": " + xcptn.getMessage
)
)
}
end loadCommonOptions
end CommonOptionsHelper
© 2015 - 2025 Weber Informatics LLC | Privacy Policy