Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ package {{invokerPackage}}
import scala.deriving.*
import scala.compiletime.*
import java.io.File
import java.util.UUID
import java.time.{LocalDate, OffsetDateTime}
import java.time.format.DateTimeFormatter
import com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, writeToString}

type Primitive = String | Short | Int | Long | Float | Double | BigDecimal |
Boolean
type Primitive = String | Short | Int | Long | Float | Double | BigDecimal | Boolean | UUID | LocalDate | OffsetDateTime

enum Authorization:
case NoAuthorization
Expand Down Expand Up @@ -45,6 +47,12 @@ private inline def checkFields[T <: Tuple]: Unit =
case _ => error("Cannot derive structure, structure must consist only of primitive fields")
}

extension (p: Primitive)
def asString: String = p match
case v: OffsetDateTime => DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(v)
case v: LocalDate => DateTimeFormatter.ISO_LOCAL_DATE.format(v)
case _ => p.toString

private val flattenKeyVals: Primitive | Option[Primitive] => Option[Primitive] = {
case p: Primitive => Some(p)
case opt: Option[Primitive] => opt
Expand Down Expand Up @@ -81,7 +89,7 @@ object FormSerializable:
optArray.map(serializeArray(name, _, format, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
freeObj.map((key, value) => (key, value.toString)).toSeq
freeObj.map((key, value) => (key, value.asString)).toSeq
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
Expand Down Expand Up @@ -115,7 +123,7 @@ object FormSerializable:
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
Seq(paramName -> value.toString) // for primitve values explode does not change anything
Seq(paramName -> value.asString) // for primitve values explode does not change anything
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support primitive values")
case FormStyleFormat.PIPEDELIMITED =>
Expand All @@ -132,14 +140,14 @@ object FormSerializable:
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(","))
inline if explode then values.map(s => (paramName, s.asString))
else Seq(paramName -> values.map(_.asString).mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(" ")) // Sttp will encode space as +, from https://swagger.io/docs/specification/v3_0/serialization/#query-parameters it is not clear if it should be + or %20
inline if explode then values.map(s => (paramName, s.asString))
else Seq(paramName -> values.map(_.asString).mkString(" ")) // Sttp will encode space as +, from https://swagger.io/docs/specification/v3_0/serialization/#query-parameters it is not clear if it should be + or %20
case FormStyleFormat.PIPEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString("|"))
inline if explode then values.map(s => (paramName, s.asString))
else Seq(paramName -> values.map(_.asString).mkString("|"))
case FormStyleFormat.DEEPOBJECT =>
error("FormStyleFormat.DeepObject does not support arrays")
}
Expand All @@ -151,14 +159,14 @@ object FormSerializable:
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then keyValPairs.map((key, value) => (key, value.toString))
else Seq(paramName -> keyValPairs.flatMap((key, value) => Seq(key, value.toString)).mkString(","))
inline if explode then keyValPairs.map((key, value) => (key, value.asString))
else Seq(paramName -> keyValPairs.flatMap((key, value) => Seq(key, value.asString)).mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support objects")
case FormStyleFormat.PIPEDELIMITED =>
error("FormStyleFormat.PipeDelimited does not support objects")
case FormStyleFormat.DEEPOBJECT =>
inline if explode then keyValPairs.map((key, value) => (s"$paramName[$key]", value.toString))
inline if explode then keyValPairs.map((key, value) => (s"$paramName[$key]", value.asString))
else error("FormStyleFormat.DeepObject does not support explode=false")
}
end FormSerializable
Expand All @@ -179,11 +187,11 @@ object HeaderSerializable:
summonFrom {
case t: HeaderSerializable[T] => t.serialize(name, obj, explode)
case _ => inline obj match
case primitive: Primitive => Map(name -> primitive.toString)
case optPrimitive: Option[Primitive] => optPrimitive.map(v => Map(name -> v.toString)).getOrElse(Map.empty[String, String])
case seqPrimitive: Seq[Primitive] => Map(name -> seqPrimitive.map(_.toString).mkString(","))
case optSeqPrimitive: Option[Seq[Primitive]] => optSeqPrimitive.map(v => Map(name -> v.map(_.toString).mkString(","))).getOrElse(Map.empty[String, String])
case mapPrimitive: Map[String, Primitive] => mapPrimitive.map((k, v) => (k, v.toString))
case primitive: Primitive => Map(name -> primitive.asString)
case optPrimitive: Option[Primitive] => optPrimitive.map(v => Map(name -> v.asString)).getOrElse(Map.empty[String, String])
case seqPrimitive: Seq[Primitive] => Map(name -> seqPrimitive.map(_.asString).mkString(","))
case optSeqPrimitive: Option[Seq[Primitive]] => optSeqPrimitive.map(v => Map(name -> v.map(_.asString).mkString(","))).getOrElse(Map.empty[String, String])
case mapPrimitive: Map[String, Primitive] => mapPrimitive.map((k, v) => (k, v.asString))
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
Expand All @@ -192,7 +200,7 @@ object HeaderSerializable:
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
.map((k, v) => (k, v.get.asString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Expand All @@ -206,7 +214,7 @@ object HeaderSerializable:
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
.map((k, v) => (k, v.get.asString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Expand Down Expand Up @@ -235,7 +243,7 @@ object PathSerializable:
optArray.map(serializeArray(name, _, style, explode))
.getOrElse("")
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, style, explode)
serializeModel(name, freeObj.map((key, value) => (key, value.asString)).toSeq, style, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
Expand Down Expand Up @@ -267,19 +275,19 @@ object PathSerializable:
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => value.toString
case PathStyleFormat.LABEL => s".${value.toString}"
case PathStyleFormat.MATRIX => s";$paramName=${value.toString}"
case PathStyleFormat.SIMPLE => value.asString
case PathStyleFormat.LABEL => s".${value.asString}"
case PathStyleFormat.MATRIX => s";$paramName=${value.asString}"

private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => values.map(_.toString).mkString(",")
case PathStyleFormat.LABEL => inline if explode then values.map(_.toString).mkString(".", ".", "") else values.map(_.toString).mkString(".", ",", "")
case PathStyleFormat.MATRIX => inline if explode then values.map(v => s";$paramName=${v.toString}").mkString else s";$paramName=" + values.map(_.toString).mkString(",")
case PathStyleFormat.SIMPLE => values.map(_.asString).mkString(",")
case PathStyleFormat.LABEL => inline if explode then values.map(_.asString).mkString(".", ".", "") else values.map(_.asString).mkString(".", ",", "")
case PathStyleFormat.MATRIX => inline if explode then values.map(v => s";$paramName=${v.asString}").mkString else s";$paramName=" + values.map(_.asString).mkString(",")

private inline def serializeModel(
paramName: String,
Expand All @@ -288,14 +296,14 @@ object PathSerializable:
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(",")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(",")
inline if explode then keyValPairs.map((k, v) => s"$k=${v.asString}").mkString(",")
else keyValPairs.map((k, v) => s"$k,${v.asString}").mkString(",")
case PathStyleFormat.LABEL =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(".", ".", "")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(".", ",", "")
inline if explode then keyValPairs.map((k, v) => s"$k=${v.asString}").mkString(".", ".", "")
else keyValPairs.map((k, v) => s"$k,${v.asString}").mkString(".", ",", "")
case PathStyleFormat.MATRIX =>
inline if explode then keyValPairs.map((k, v) => s";$k=${v.toString}").mkString
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(s";$paramName=", ",", "")
inline if explode then keyValPairs.map((k, v) => s";$k=${v.asString}").mkString
else keyValPairs.map((k, v) => s"$k,${v.asString}").mkString(s";$paramName=", ",", "")
end PathSerializable

trait CookieSerializable[T]:
Expand Down Expand Up @@ -326,7 +334,7 @@ object CookieSerializable:
optArray.map(serializeArray(name, _, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, explode)
serializeModel(name, freeObj.map((key, value) => (key, value.asString)).toSeq, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
Expand Down Expand Up @@ -356,23 +364,23 @@ object CookieSerializable:
paramName: String,
value: Primitive,
inline explode: Boolean
): Seq[(String, String)] = Seq(paramName -> value.toString)
): Seq[(String, String)] = Seq(paramName -> value.asString)

private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> values.map(_.toString).mkString(","))
else Seq(paramName -> values.map(_.asString).mkString(","))

private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> keyValPairs.map((k, v) => s"$k,$v").mkString(","))
else Seq(paramName -> keyValPairs.map((k, v) => s"$k,${v.asString}").mkString(","))
end CookieSerializable

object Helpers:
Expand Down
Loading
Loading