Skip to content

Commit

Permalink
Merge pull request #312 from dos65/ci_fmt
Browse files Browse the repository at this point in the history
enforce fmt checks on ci
  • Loading branch information
dos65 authored Aug 29, 2024
2 parents 3676515 + 88a6cf8 commit 7370dd6
Show file tree
Hide file tree
Showing 81 changed files with 7,203 additions and 2,267 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ jobs:

- name: Test
run: sbt ++${{matrix.scala}} test

fmt:
name: Fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- name: Fmt
run: sbt "scalafmtCheckAll;scalafmtSbtCheck"

publish:
name: Publish Artifacts
Expand Down
29 changes: 15 additions & 14 deletions modules/circe/src/main/scala/io.circe/JsonNumberHack.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import tethys.writers.tokens.TokenWriter

trait JsonNumberHack {
protected def writeNumber(number: JsonNumber, writer: TokenWriter): Unit = {
if(JsonNumberHack.isHackCompatible) {
if (JsonNumberHack.isHackCompatible) {
number match {
case value: JsonDecimal =>
writer.writeRawJson(value.toString)
Expand All @@ -27,17 +27,18 @@ trait JsonNumberHack {
}

object JsonNumberHack {
private val isHackCompatible: Boolean = try {
val loader = getClass.getClassLoader
loader.loadClass("io.circe.BiggerDecimalJsonNumber")
loader.loadClass("io.circe.JsonDecimal")
loader.loadClass("io.circe.JsonBiggerDecimal")
loader.loadClass("io.circe.JsonBigDecimal")
loader.loadClass("io.circe.JsonLong")
loader.loadClass("io.circe.JsonDouble")
loader.loadClass("io.circe.JsonFloat")
true
} catch {
case _: ClassNotFoundException => false
}
private val isHackCompatible: Boolean =
try {
val loader = getClass.getClassLoader
loader.loadClass("io.circe.BiggerDecimalJsonNumber")
loader.loadClass("io.circe.JsonDecimal")
loader.loadClass("io.circe.JsonBiggerDecimal")
loader.loadClass("io.circe.JsonBigDecimal")
loader.loadClass("io.circe.JsonLong")
loader.loadClass("io.circe.JsonDouble")
loader.loadClass("io.circe.JsonFloat")
true
} catch {
case _: ClassNotFoundException => false
}
}
100 changes: 58 additions & 42 deletions modules/circe/src/main/scala/tethys/circe/ast/CirceSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,73 @@ import tethys.{JsonObjectWriter, JsonReader, JsonWriter}
import tethys.writers.tokens.TokenWriter

trait CirceSupport {
implicit lazy val circeJsonObjectWriter: JsonWriter[JsonObject] = new JsonObjectWriter[JsonObject] {
def writeValues(value: JsonObject, writer: TokenWriter): Unit = {
val folder = new TethysJsonFolder(writer)
val it = value.toIterable.iterator
while (it.hasNext) {
val (k, v) = it.next()

writer.writeFieldName(k)
v.foldWith(folder)
implicit lazy val circeJsonObjectWriter: JsonWriter[JsonObject] =
new JsonObjectWriter[JsonObject] {
def writeValues(value: JsonObject, writer: TokenWriter): Unit = {
val folder = new TethysJsonFolder(writer)
val it = value.toIterable.iterator
while (it.hasNext) {
val (k, v) = it.next()

writer.writeFieldName(k)
v.foldWith(folder)
}
}
}
}

implicit lazy val circeJsonWriter: JsonWriter[Json] = new JsonWriter[Json] {
def write(value: Json, writer: TokenWriter): Unit = value.foldWith(new TethysJsonFolder(writer))
def write(value: Json, writer: TokenWriter): Unit =
value.foldWith(new TethysJsonFolder(writer))
}

implicit lazy val circeJsonObjectReader: JsonReader[JsonObject] = new JsonReader[JsonObject] {
def read(it: TokenIterator)(implicit fieldName: FieldName): JsonObject =
if (!it.currentToken().isObjectStart) ReaderError.wrongJson(s"Expected object start but found: ${it.currentToken()}")
else {
it.next()

var builder = ArrayBuffer.newBuilder[(String, Json)]
while (!it.currentToken().isObjectEnd) {
val token = it.currentToken()
if (token.isFieldName) {
val name = it.fieldName()
val value = circeJsonReader.read(it.next())(fieldName.appendFieldName(name))

builder += ((name, value))
implicit lazy val circeJsonObjectReader: JsonReader[JsonObject] =
new JsonReader[JsonObject] {
def read(it: TokenIterator)(implicit fieldName: FieldName): JsonObject =
if (!it.currentToken().isObjectStart)
ReaderError.wrongJson(
s"Expected object start but found: ${it.currentToken()}"
)
else {
it.next()

var builder = ArrayBuffer.newBuilder[(String, Json)]
while (!it.currentToken().isObjectEnd) {
val token = it.currentToken()
if (token.isFieldName) {
val name = it.fieldName()
val value =
circeJsonReader.read(it.next())(fieldName.appendFieldName(name))

builder += ((name, value))
} else
ReaderError.wrongJson(
s"Expect end of object or field name but '$token' found"
)(fieldName)
}
else ReaderError.wrongJson(s"Expect end of object or field name but '$token' found")(fieldName)
}
it.next()
it.next()

JsonObject.fromIterable(builder.result)
}
}
JsonObject.fromIterable(builder.result)
}
}

implicit lazy val circeJsonReader: JsonReader[Json] = new JsonReader[Json] {
def read(it: TokenIterator)(implicit fieldName: FieldName): Json = {
val token = it.currentToken()

if (token.isObjectStart) Json.fromJsonObject(circeJsonObjectReader.read(it))
else if (token.isArrayStart) Json.fromValues(JsonReader[Vector[Json]].read(it))
else if (token.isStringValue) Json.fromString(JsonReader.stringReader.read(it))
else if (token.isBooleanValue) Json.fromBoolean(JsonReader.booleanReader.read(it))
else if (token.isNumberValue) JsonReader.numberReader.read(it) match {
case x@(_: java.lang.Byte | _: java.lang.Short | _: java.lang.Long) => Json.fromLong(x.longValue)
case x: java.lang.Integer => Json.fromInt(x)
case x: java.lang.Float => Json.fromFloatOrNull(x)
case x: java.lang.Double => Json.fromDoubleOrNull(x)
if (token.isObjectStart)
Json.fromJsonObject(circeJsonObjectReader.read(it))
else if (token.isArrayStart)
Json.fromValues(JsonReader[Vector[Json]].read(it))
else if (token.isStringValue)
Json.fromString(JsonReader.stringReader.read(it))
else if (token.isBooleanValue)
Json.fromBoolean(JsonReader.booleanReader.read(it))
else if (token.isNumberValue) JsonReader.numberReader.read(it) match {
case x @ (_: java.lang.Byte | _: java.lang.Short | _: java.lang.Long) =>
Json.fromLong(x.longValue)
case x: java.lang.Integer => Json.fromInt(x)
case x: java.lang.Float => Json.fromFloatOrNull(x)
case x: java.lang.Double => Json.fromDoubleOrNull(x)

case x: java.math.BigInteger => Json.fromBigInt(x)
case x: BigInt => Json.fromBigInt(x)
Expand All @@ -69,12 +83,14 @@ trait CirceSupport {
case x: BigDecimal => Json.fromBigDecimal(x)
case x => Json.fromBigDecimal(x.doubleValue)
}
else if (token.isNullValue) { it.next(); Json.Null }
else if (token.isNullValue) { it.next(); Json.Null }
else ReaderError.wrongJson(s"Unexpected token found: $token")(fieldName)
}
}

private[this] class TethysJsonFolder(writer: TokenWriter) extends Json.Folder[Unit] with JsonNumberHack {
private[this] class TethysJsonFolder(writer: TokenWriter)
extends Json.Folder[Unit]
with JsonNumberHack {
def onNull: Unit = writer.writeNull()
def onBoolean(value: Boolean): Unit = writer.writeBoolean(value)
def onNumber(value: JsonNumber): Unit = writeNumber(value, writer)
Expand Down
46 changes: 29 additions & 17 deletions modules/circe/src/test/scala/tethys/circe/CirceSupportTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ class CirceSupportTest extends AnyFlatSpec with Matchers {
}

it should "parse Double" in {
token(100.0D).tokensAs[Json] shouldBe Json.fromDoubleOrNull(100.0D)
token(100.0d).tokensAs[Json] shouldBe Json.fromDoubleOrNull(100.0d)
}

it should "parse BigInt" in {
token(BigInt(100L)).tokensAs[Json] shouldBe Json.fromBigInt(BigInt(100L))
}

it should "parse BigDecimal" in {
token(BigDecimal(100.0D)).tokensAs[Json] shouldBe Json.fromBigDecimal(100.0D)
token(BigDecimal(100.0d)).tokensAs[Json] shouldBe Json.fromBigDecimal(
100.0d
)
}

it should "parse String" in {
Expand All @@ -52,7 +54,9 @@ class CirceSupportTest extends AnyFlatSpec with Matchers {

it should "parse Array" in {
arr(1, 2L, 3).tokensAs[Json] shouldBe
Json.fromValues(List(Json.fromLong(1L), Json.fromLong(2L), Json.fromLong(3L)))
Json.fromValues(
List(Json.fromLong(1L), Json.fromLong(2L), Json.fromLong(3L))
)
}

it should "parse JsonObject" in {
Expand All @@ -72,13 +76,15 @@ class CirceSupportTest extends AnyFlatSpec with Matchers {
}

it should "parse Array of JsonObject" in {
arr(obj("a" -> "b"), obj("c" -> "d")).tokensAs[Json] shouldBe Json.fromValues(List(
Json.fromJsonObject(JsonObject("a" -> Json.fromString("b"))),
Json.fromJsonObject(JsonObject("c" -> Json.fromString("d")))
))
arr(obj("a" -> "b"), obj("c" -> "d"))
.tokensAs[Json] shouldBe Json.fromValues(
List(
Json.fromJsonObject(JsonObject("a" -> Json.fromString("b"))),
Json.fromJsonObject(JsonObject("c" -> Json.fromString("d")))
)
)
}


behavior of "Circe ast JsonWriter"

it should "write Int" in {
Expand All @@ -94,19 +100,21 @@ class CirceSupportTest extends AnyFlatSpec with Matchers {
}

it should "write Double" in {
Json.fromDouble(100.0D).asTokenList shouldBe token(100.0D)
Json.fromDouble(100.0d).asTokenList shouldBe token(100.0d)
}

it should "write BigInt" in {
Json.fromBigInt(BigInt("10000000000")).asTokenList match {
case DoubleValueNode(d) :: Nil => d shouldBe 1.0e10 // 2.11 only behavior
case LongValueNode(l) :: Nil => l shouldBe 10000000000L
case _ => fail()
case LongValueNode(l) :: Nil => l shouldBe 10000000000L
case _ => fail()
}
}

it should "write BigDecimal" in {
Json.fromBigDecimal(BigDecimal(100.0D)).asTokenList shouldBe token(BigDecimal(100.0D))
Json.fromBigDecimal(BigDecimal(100.0d)).asTokenList shouldBe token(
BigDecimal(100.0d)
)
}

it should "write String" in {
Expand All @@ -126,11 +134,15 @@ class CirceSupportTest extends AnyFlatSpec with Matchers {
}

it should "write Array" in {
Json.fromValues(List(
Json.fromInt(1),
Json.fromInt(2),
Json.fromInt(3)
)).asTokenList shouldBe arr(1L, 2L, 3L)
Json
.fromValues(
List(
Json.fromInt(1),
Json.fromInt(2),
Json.fromInt(3)
)
)
.asTokenList shouldBe arr(1L, 2L, 3L)
}

it should "write JsonObject" in {
Expand Down
33 changes: 19 additions & 14 deletions modules/core/src/main/scala-3/tethys/FieldStyle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package tethys

import java.util.regex.Pattern


enum FieldStyle {
case Capitalize, Uncapitalize, LowerCase, UpperCase

Expand All @@ -11,36 +10,42 @@ enum FieldStyle {
case SnakeCase, LowerSnakeCase, UpperSnakeCase, CapitalizedSnakeCase
}

private[tethys]
object FieldStyle:
private[tethys] object FieldStyle:
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 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: FieldStyle): String =
style match
case FieldStyle.Capitalize => capitalize(string)
case FieldStyle.Capitalize => capitalize(string)
case FieldStyle.Uncapitalize => uncapitalize(string)
case FieldStyle.LowerCase => lowercase(string)
case FieldStyle.UpperCase => uppercase(string)
case FieldStyle.LowerCase => lowercase(string)
case FieldStyle.UpperCase => uppercase(string)

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

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

case FieldStyle.CapitalizedSnakeCase =>
(snakeCase andThen capitalize)(string)

private def splitName(name: String): List[String] =
val first = FieldStyle.regexp1.matcher(name).replaceAll(FieldStyle.replacement)
FieldStyle.regexp2.matcher(first).replaceAll(FieldStyle.replacement).split("_").toList
val first =
FieldStyle.regexp1.matcher(name).replaceAll(FieldStyle.replacement)
FieldStyle.regexp2
.matcher(first)
.replaceAll(FieldStyle.replacement)
.split("_")
.toList
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ object OrdinalEnumJsonReader:
case ex: NoSuchElementException =>
ReaderError.wrongJson(s"Unknown enum ordinal: $res")
else
ReaderError.wrongJson(s"Expected int value but found: ${it.currentToken()}")
ReaderError.wrongJson(
s"Expected int value but found: ${it.currentToken()}"
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package tethys

import tethys.writers.tokens.TokenWriter


trait OrdinalEnumJsonWriter[A] extends JsonWriter[A]

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

inline def withLabel[A <: scala.reflect.Enum](label: String): JsonObjectWriter[A] =
inline def withLabel[A <: scala.reflect.Enum](
label: String
): JsonObjectWriter[A] =
(value: A, tokenWriter: writers.tokens.TokenWriter) =>
tokenWriter.writeFieldName(label)
tokenWriter.writeNumber(value.ordinal)
Loading

0 comments on commit 7370dd6

Please sign in to comment.