zio.http.UrlInterpolator.scala Maven / Gradle / Ivy
/*
* Copyright 2021 - 2023 Sporta Technologies PVT LTD & the ZIO HTTP contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.http
import scala.quoted.*
trait UrlInterpolator {
extension(inline sc: StringContext) {
inline def url(inline args: Any*): URL = ${ UrlInterpolatorMacro.url('sc, 'args) }
}
}
private[http] object UrlInterpolatorMacro {
def url(sc: Expr[StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[URL] = {
import quotes.reflect.*
import report.*
val ctx = sc.valueOrAbort
val staticParts = ctx.parts
val argExprs = args match {
case Varargs(exprs) => exprs
case _ => errorAndAbort(s"Unexpected arguments", args)
}
val result = if (argExprs.isEmpty) {
URL.decode(staticParts.mkString) match {
case Left(error) => errorAndAbort(s"Invalid URL: $error", sc)
case Right(url) =>
val uri = Expr(url.encode)
'{ URL.fromURI(new java.net.URI($uri)).get }
}
} else {
val injectedPartExamples =
argExprs.map { arg =>
val typ = arg.asTerm.tpe.asType
typ match {
case '[String] =>
"string"
case '[Byte] =>
"123"
case '[Short] =>
"1234"
case '[Int] =>
"1234"
case '[Long] =>
"1234"
case '[Boolean] =>
"true"
case '[Float] =>
"1.23"
case '[Double] =>
"1.23"
case '[java.util.UUID] =>
"123e4567-e89b-12d3-a456-426614174000"
case _ =>
errorAndAbort(s"Injected field ${arg.show} has an unsupported type", arg)
}
}
val exampleParts = staticParts.zipAll(injectedPartExamples, "", "").flatMap { case (a, b) => List(a, b) }
val example = exampleParts.mkString
URL.decode(example) match {
case Left(error) =>
errorAndAbort(s"Invalid URL: $error", sc)
case Right(url) =>
val parts =
staticParts.map { s => Expr(s) }
.zipAll(argExprs, Expr(""), Expr(""))
.flatMap { case (a, b) => List(a, b) }
val concatenated =
parts.foldLeft[Expr[String]](Expr("")) { case (acc, part) =>
'{$acc + $part}
}
'{ URL.fromURI(new java.net.URI($concatenated)).get }
}
}
result
}
}