Skip to content

Commit

Permalink
Implemented configurable derivation
Browse files Browse the repository at this point in the history
  • Loading branch information
Георгий Ковалев committed May 3, 2024
1 parent b90f917 commit 26b3c00
Show file tree
Hide file tree
Showing 36 changed files with 2,103 additions and 360 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import json.bench.{DataReader, DataWriter}
*/
object TethysBench {

implicit val dataWriter: JsonWriter[Data] = jsonWriter[Data]
implicit val dataReader: JsonReader[Data] = jsonReader[Data]
implicit val dataWriter: JsonWriter[Data] = JsonWriter.derived[Data]
implicit val dataReader: JsonReader[Data] = JsonReader.derived[Data]

object TethysJacksonDataProcessor extends DataWriter with DataReader {

Expand Down
Binary file added modules/core/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package tethys.derivation


private [tethys] trait JsonObjectWriterDerivation {

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package tethys.derivation


private [tethys] trait JsonObjectWriterDerivation {

}

This file was deleted.

10 changes: 10 additions & 0 deletions modules/core/src/main/scala-3/tethys/JsonConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package tethys

sealed trait JsonConfig[A]:
def discriminateBy[Field](select: A => Field): JsonConfig[A]


object JsonConfig:
@scala.annotation.compileTimeOnly("Config must be an inlined given or provided directly to 'derived'")
def configure[A]: JsonConfig[A] =
throw IllegalStateException("Config must be an inlined given or provided directly to 'derived'")
46 changes: 46 additions & 0 deletions modules/core/src/main/scala-3/tethys/JsonFieldStyle.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tethys

import java.util.regex.Pattern


enum JsonFieldStyle {
case Capitalize, Uncapitalize, LowerCase, UpperCase

case KebabCase, LowerKebabCase, UpperKebabCase, CapitalizedKebabCase

case SnakeCase, LowerSnakeCase, UpperSnakeCase, CapitalizedSnakeCase
}

private[tethys]
object JsonFieldStyle:
private val regexp1: Pattern = Pattern.compile("([A-Z]+)([A-Z][a-z])")
private val regexp2: Pattern = Pattern.compile("([a-z\\d])([A-Z])")
private val replacement: String = "$1_$2"
private val snakeCase: String => String = splitName(_).mkString("_")
private val kebabcase: String => String = splitName(_).mkString("-")
private val capitalize: String => String = _.capitalize
private val uncapitalize: String => String = (field: String) => field.updated(0, field.charAt(0).toLower)
private val lowercase: String => String = _.toLowerCase()
private val uppercase: String => String = _.toUpperCase()

def applyStyle(string: String, style: JsonFieldStyle): String =
style match
case JsonFieldStyle.Capitalize => capitalize(string)
case JsonFieldStyle.Uncapitalize => uncapitalize(string)
case JsonFieldStyle.LowerCase => lowercase(string)
case JsonFieldStyle.UpperCase => uppercase(string)

case JsonFieldStyle.KebabCase => kebabcase(string)
case JsonFieldStyle.LowerKebabCase => (kebabcase andThen lowercase)(string)
case JsonFieldStyle.UpperKebabCase => (kebabcase andThen uppercase)(string)
case JsonFieldStyle.CapitalizedKebabCase => (kebabcase andThen capitalize)(string)

case JsonFieldStyle.SnakeCase => snakeCase(string)
case JsonFieldStyle.LowerSnakeCase => (snakeCase andThen lowercase)(string)
case JsonFieldStyle.UpperSnakeCase => (snakeCase andThen uppercase)(string)
case JsonFieldStyle.CapitalizedSnakeCase => (snakeCase andThen capitalize)(string)


private def splitName(name: String): List[String] =
val first = JsonFieldStyle.regexp1.matcher(name).replaceAll(JsonFieldStyle.replacement)
JsonFieldStyle.regexp2.matcher(first).replaceAll(JsonFieldStyle.replacement).split("_").toList
21 changes: 21 additions & 0 deletions modules/core/src/main/scala-3/tethys/OrdinalEnumJsonReader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package tethys

import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.TokenIterator

trait OrdinalEnumJsonReader[A] extends JsonReader[A]

object OrdinalEnumJsonReader:
inline def derived[A <: scala.reflect.Enum]: OrdinalEnumJsonReader[A] =
new OrdinalEnumJsonReader[A]:
def read(it: TokenIterator)(implicit fieldName: FieldName): A =
if it.currentToken().isNumberValue then
val res = it.int()
it.next()
try derivation.EnumCompanion.getByOrdinal[A](res)
catch
case ex: NoSuchElementException =>
ReaderError.wrongJson(s"Unknown enum ordinal: $res")
else
ReaderError.wrongJson(s"Expected int value but found: ${it.currentToken()}")

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package tethys
import tethys.writers.tokens.TokenWriter


trait OrdinalEnumWriter[A] extends JsonWriter[A]
trait OrdinalEnumJsonWriter[A] extends JsonWriter[A]

object OrdinalEnumWriter:
inline def derived[A <: scala.reflect.Enum]: OrdinalEnumWriter[A] =
object OrdinalEnumJsonWriter:
inline def derived[A <: scala.reflect.Enum]: OrdinalEnumJsonWriter[A] =
(value: A, tokenWriter: TokenWriter) => tokenWriter.writeNumber(value.ordinal)

inline def withLabel[A <: scala.reflect.Enum](label: String): JsonObjectWriter[A] =
Expand Down
18 changes: 0 additions & 18 deletions modules/core/src/main/scala-3/tethys/OrdinalEnumReader.scala

This file was deleted.

23 changes: 23 additions & 0 deletions modules/core/src/main/scala-3/tethys/StringEnumJsonReader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package tethys

import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.TokenIterator

trait StringEnumJsonReader[A] extends JsonReader[A]

object StringEnumJsonReader:
inline def derived[A <: scala.reflect.Enum]: StringEnumJsonReader[A] =
new StringEnumJsonReader[A]:
def read(it: TokenIterator)(implicit fieldName: FieldName): A =
if it.currentToken().isStringValue then
val res = it.string()
it.next()
try
derivation.EnumCompanion.getByName[A](res)
catch
case ex: NoSuchElementException =>
ReaderError.wrongJson(s"Unknown enum name: $res")
else
ReaderError.wrongJson(s"Expected string value but found: ${it.currentToken()}")


Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package tethys
import tethys.writers.tokens.TokenWriter

trait StringEnumWriter[A] extends JsonWriter[A]
trait StringEnumJsonWriter[A] extends JsonWriter[A]

object StringEnumWriter:
inline def derived[A <: scala.reflect.Enum]: StringEnumWriter[A] =
object StringEnumJsonWriter:
inline def derived[A <: scala.reflect.Enum]: StringEnumJsonWriter[A] =
(value: A, tokenWriter: TokenWriter) => tokenWriter.writeString(value.toString)

inline def withLabel[A <: scala.reflect.Enum](label: String): JsonObjectWriter[A] =
Expand Down
18 changes: 0 additions & 18 deletions modules/core/src/main/scala-3/tethys/StringEnumReader.scala

This file was deleted.

Loading

0 comments on commit 26b3c00

Please sign in to comment.