scalaintermediate

Custom String Interpolation

Create custom string interpolators in Scala: SQL, HTML, JSON, and validated interpolation.

scala
// Custom string interpolators via extension on StringContext

// SQL interpolator with parameter binding
case class SqlQuery(sql: String, params: List[Any])

extension (sc: StringContext)
  def sql(args: Any*): SqlQuery =
    val parts = sc.parts.iterator
    val params = args.iterator
    val sb = StringBuilder()
    val paramList = scala.collection.mutable.ListBuffer.empty[Any]

    sb.append(parts.next())
    while parts.hasNext do
      paramList += params.next()
      sb.append("?")
      sb.append(parts.next())

    SqlQuery(sb.toString(), paramList.toList)

// HTML interpolator with escaping
extension (sc: StringContext)
  def html(args: Any*): String =
    def escape(s: String): String =
      s.replace("&", "&")
       .replace("<", "&lt;")
       .replace(">", "&gt;")
       .replace("\"", "&quot;")
       .replace("'", "&#39;")

    val parts = sc.parts.iterator
    val escaped = args.iterator
    val sb = StringBuilder()
    sb.append(parts.next())
    while parts.hasNext do
      sb.append(escape(escaped.next().toString))
      sb.append(parts.next())
    sb.toString()

// Logging interpolator
enum LogLevel:
  case DEBUG, INFO, WARN, ERROR

extension (sc: StringContext)
  def log(args: Any*): (LogLevel, String) =
    val msg = sc.s(args*)
    val level = msg.takeWhile(_ != ':') match
      case "DEBUG" => LogLevel.DEBUG
      case "WARN"  => LogLevel.WARN
      case "ERROR" => LogLevel.ERROR
      case _       => LogLevel.INFO
    (level, msg)

// Regex interpolator
extension (sc: StringContext)
  def r(args: Any*): scala.util.matching.Regex =
    sc.s(args*).r

@main def run(): Unit =
  // SQL interpolator
  val name = "Alice"
  val age = 30
  val query = sql"SELECT * FROM users WHERE name = $name AND age > $age"
  println(s"SQL: ${query.sql}")
  println(s"Params: ${query.params}")

  val id = 42
  val update = sql"UPDATE users SET active = true WHERE id = $id"
  println(s"\nUpdate SQL: ${update.sql}")
  println(s"Params: ${update.params}")

  // HTML interpolator (auto-escapes)
  val userInput = "<script>alert('xss')</script>"
  val title = "Hello & Goodbye"
  val page = html"<div><h1>$title</h1><p>$userInput</p></div>"
  println(s"\nHTML: $page")

  // Standard interpolators
  val pi = math.Pi
  println(f"\nPi: $pi%.4f")
  println(s"Name: $name, Age: $age")
  println(raw"Newline: \n Tab: \t (raw, no escape)")

  // Regex interpolator
  val emailRegex = r"[a-zA-Z0-9.]+@[a-zA-Z0-9.]+"
  val text = "Contact alice@test.com or bob@example.org"
  val found = emailRegex.findAllIn(text).toList
  println(s"\nFound emails: $found")

  // Multi-line
  val items = List("apple", "banana", "cherry")
  val list = items.zipWithIndex.map((item, i) =>
    html"<li>${i + 1}. $item</li>"
  ).mkString("\n")
  println(s"\nList:\n$list")

Use Cases

  • SQL query building with parameter binding
  • XSS-safe HTML generation
  • Domain-specific string formatting

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.