Skip to content

Commit

Permalink
feat: Configurable error messages (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
Iltotore authored Jun 20, 2024
1 parent 37336d9 commit 249be76
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
19 changes: 19 additions & 0 deletions main/src/io/github/iltotore/iron/internal/IronConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.iltotore.iron.internal

/**
* The config or Iron at compile-time.
*
* @param color enable colored messages
* @param shortMessages use abbreviated messages, useful for error lenses and similar
*/
case class IronConfig(color: Boolean, shortMessages: Boolean)

object IronConfig:

/**
* The config as defined by the properties/environment.
*/
val fromSystem: IronConfig = IronConfig(
color = sys.props.get("iron.color").orElse(sys.env.get("IRON_COLOR")).flatMap(_.toBooleanOption).getOrElse(true),
shortMessages = sys.props.get("iron.shortMessages").orElse(sys.env.get("IRON_SHORT_MESSAGES")).flatMap(_.toBooleanOption).getOrElse(false),
)
4 changes: 3 additions & 1 deletion main/src/io/github/iltotore/iron/internal/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ package io.github.iltotore.iron.internal

extension (text: String)

def colorized(color: String): String = s"$color$text${Console.RESET}"
def colorized(color: String)(using config: IronConfig): String =
if config.color then s"$color$text${Console.RESET}"
else text
44 changes: 28 additions & 16 deletions main/src/io/github/iltotore/iron/macros/package.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.iltotore.iron.macros

import io.github.iltotore.iron.internal.{IronConfig, colorized}
import scala.Console.{MAGENTA, RESET}
import scala.quoted.*

Expand All @@ -18,40 +19,51 @@ private def assertConditionImpl[A: Type](input: Expr[A], cond: Expr[Boolean], me

import quotes.reflect.*

given Printer[Tree] = Printer.TreeAnsiCode
given config: IronConfig = IronConfig.fromSystem
given Printer[Tree] =
if config.color then Printer.TreeAnsiCode
else Printer.TreeCode

val rflUtil = reflectUtil(using quotes)
import rflUtil.*

val inputType = TypeRepr.of[A]

val messageValue = message.decode.getOrElse("<Unknown message>")
val condValue = cond.decode
.fold(
err => compileTimeError(

def condError(failure: DecodingFailure): Nothing =
if config.shortMessages then
report.errorAndAbort("Cannot refine value at compile-time.")
else
compileTimeError(
s"""Cannot refine value at compile-time because the predicate cannot be evaluated.
|This is likely because the condition or the input value isn't fully inlined.
|
|To test a constraint at runtime, use one of the `refine...` extension methods.
|
|${MAGENTA}Inlined input$RESET: ${input.asTerm.show}
|${MAGENTA}Inlined condition$RESET: ${cond.asTerm.show}
|${MAGENTA}Message$RESET: $messageValue
|${MAGENTA}Reason$RESET: ${err.prettyPrint()}""".stripMargin
),
identity
)
|${"Inlined input".colorized(MAGENTA)}: ${input.asTerm.show}
|${"Inlined condition".colorized(MAGENTA)}: ${cond.asTerm.show}
|${"Message".colorized(MAGENTA)}: $messageValue
|${"Reason".colorized(MAGENTA)}: ${failure.prettyPrint()}""".stripMargin
)

val inputValue = input.decode.toOption
val condValue = cond.decode.fold(condError, identity)

if !condValue then
compileTimeError(s"""|Could not satisfy a constraint for type $MAGENTA${inputType.show}$RESET.
|
|${MAGENTA}Value$RESET: ${input.asTerm.show}
|${MAGENTA}Message$RESET: $messageValue""".stripMargin)
if config.shortMessages then
report.errorAndAbort(s"$messageValue: ${inputValue.getOrElse(input.show)}")
else
compileTimeError(s"""|Could not satisfy a constraint for type ${inputType.show.colorized(MAGENTA)}.
|
|${"Value".colorized(MAGENTA)}: ${inputValue.getOrElse(input.show)}
|${"Message".colorized(MAGENTA)}: $messageValue""".stripMargin)

'{}

def compileTimeError(msg: String)(using Quotes): Nothing =
quotes.reflect.report.errorAndAbort(
s"""|-- Constraint Error --------------------------------------------------------
|$msg
|----------------------------------------------------------------------------""".stripMargin
)
)

0 comments on commit 249be76

Please sign in to comment.