Skip to content

Commit

Permalink
Unify Rating type
Browse files Browse the repository at this point in the history
  • Loading branch information
lenguyenthanh committed Jun 9, 2024
1 parent 1cbd9b5 commit e3cfa9c
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 48 deletions.
5 changes: 4 additions & 1 deletion modules/api/src/main/scala/providers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package fide.spec

import cats.syntax.all.*
import fide.spec.*
import fide.types.*
import fide.types.{ NonEmptySet, PositiveInt }
import smithy4s.*

object providers:
Expand All @@ -15,6 +15,9 @@ object providers:
given RefinementProvider[PageSizeFormat, Int, PositiveInt] =
Refinement.drivenBy(PositiveInt.either, _.value)

given RefinementProvider[RatingFormat, Int, fide.types.Rating] =
Refinement.drivenBy(fide.types.Rating.either, _.value)

given [A]: RefinementProvider[NonEmptySetFormat, Set[A], NonEmptySet[A]] =
Refinement.drivenBy[NonEmptySetFormat](
NonEmptySet.either,
Expand Down
10 changes: 9 additions & 1 deletion modules/api/src/main/smithy/_global.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ structure InternalServerError {
integer PlayerId
string FederationId

@range(min: 0, max: 4000)
@trait(selector: "integer")
@refinement(
targetType: "fide.types.Rating"
providerImport: "fide.spec.providers.given"
)
structure RatingFormat {}

@RatingFormat
@unwrap
integer Rating

@trait(selector: "string")
Expand Down
10 changes: 5 additions & 5 deletions modules/backend/src/main/scala/service.federation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import cats.syntax.all.*
import fide.db.Db
import fide.domain.Models.Pagination
import fide.domain.{ FederationSummary, Models }
import fide.spec.*
import fide.types.PositiveInt
import fide.spec.{ Rating as _, * }
import fide.types.*
import io.github.arainko.ducktape.*
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.syntax.*
Expand Down Expand Up @@ -35,9 +35,9 @@ class FederationServiceImpl(db: Db)(using Logger[IO]) extends FederationService[
val sorting = Models.Sorting.fromOption(sortBy.map(_.to[Models.SortBy]), order.map(_.to[Models.Order]))
val filter = Models.PlayerFilter(
isActive,
Models.RatingRange(standardMin.map(_.value), standardMax.map(_.value)),
Models.RatingRange(rapidMin.map(_.value), rapidMax.map(_.value)),
Models.RatingRange(blitzMin.map(_.value), blitzMax.map(_.value)),
Models.RatingRange(standardMin, standardMax),
Models.RatingRange(rapidMin, rapidMax),
Models.RatingRange(blitzMin, blitzMax),
id.value.some
)
name
Expand Down
9 changes: 4 additions & 5 deletions modules/backend/src/main/scala/service.player.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.effect.*
import cats.syntax.all.*
import fide.db.Db
import fide.domain.Models
import fide.spec.*
import fide.spec.{ Rating as _, * }
import fide.types.*
import io.github.arainko.ducktape.*
import org.typelevel.log4cats.Logger
Expand Down Expand Up @@ -35,9 +35,9 @@ class PlayerServiceImpl(db: Db)(using Logger[IO]) extends PlayerService[IO]:
val sorting = Models.Sorting.fromOption(sortBy.map(_.to[Models.SortBy]), order.map(_.to[Models.Order]))
val filter = Models.PlayerFilter(
isActive,
Models.RatingRange(standardMin.map(_.value), standardMax.map(_.value)),
Models.RatingRange(rapidMin.map(_.value), rapidMax.map(_.value)),
Models.RatingRange(blitzMin.map(_.value), blitzMax.map(_.value)),
Models.RatingRange(standardMin, standardMax),
Models.RatingRange(rapidMin, rapidMax),
Models.RatingRange(blitzMin, blitzMax),
None
)
name
Expand Down Expand Up @@ -70,7 +70,6 @@ class PlayerServiceImpl(db: Db)(using Logger[IO]) extends PlayerService[IO]:
.map(GetPlayerByIdsOutput.apply)

object PlayerTransformers:
given Transformer.Derived[Int, Rating] = Transformer.Derived.FromFunction(Rating.apply)
given Transformer.Derived[String, FederationId] = Transformer.Derived.FromFunction(FederationId.apply)
given Transformer.Derived[Int, PlayerId] = Transformer.Derived.FromFunction(PlayerId.apply)
given Transformer.Derived[OffsetDateTime, Timestamp] =
Expand Down
5 changes: 3 additions & 2 deletions modules/crawler/src/main/scala/Crawler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.effect.IO
import cats.syntax.all.*
import fide.db.{ Db, KVStore }
import fide.domain.*
import fide.types.Rating
import org.http4s.*
import org.http4s.client.Client
import org.http4s.implicits.*
Expand Down Expand Up @@ -69,8 +70,8 @@ object Downloader:
.handleErrorWith(e => error"Error while parsing line: $line, error: $e".as(none))

def parse(line: String): Option[(NewPlayer, Option[NewFederation])] =
def string(start: Int, end: Int) = line.substring(start, end).trim.some.filter(_.nonEmpty)
def number(start: Int, end: Int) = string(start, end).flatMap(_.toIntOption)
def string(start: Int, end: Int) = line.substring(start, end).trim.some.filter(_.nonEmpty)
def number(start: Int, end: Int): Option[Rating] = string(start, end).flatMap(Rating.fromString)
for
id <- number(0, 15)
name <- string(15, 76).map(_.filterNot(_.isDigit).trim)
Expand Down
54 changes: 28 additions & 26 deletions modules/db/src/main/scala/Db.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.effect.*
import cats.syntax.all.*
import fide.domain.*
import fide.domain.Models.*
import fide.types.*
import org.typelevel.log4cats.Logger
import skunk.*

Expand Down Expand Up @@ -98,8 +99,31 @@ private object Codecs:
import skunk.codec.all.*
import skunk.data.{ Arr, Type }

val title: Codec[Title] = `enum`[Title](_.value, Title.apply, Type("title"))
val sex: Codec[Sex] = `enum`[Sex](_.value, Sex.apply, Type("sex"))
// copy from https://github.com/Iltotore/iron/blob/main/skunk/src/io.github.iltotore.iron/skunk.scala for skunk 1.0.0
import io.github.iltotore.iron.*

/** Explicit conversion for refining a [[Codec]]. Decodes to the underlying type then checks the constraint.
*
* @param constraint
* the [[Constraint]] implementation to test the decoded value
*/
extension [A](codec: Codec[A])
inline def refined[C](using inline constraint: Constraint[A, C]): Codec[A :| C] =
codec.eimap[A :| C](_.refineEither[C])(_.asInstanceOf[A])

/** A [[Codec]] for refined types. Decodes to the underlying type then checks the constraint.
*
* @param codec
* the [[Codec]] of the underlying type
* @param constraint
* the [[Constraint]] implementation to test the decoded value
*/
inline given [A, C](using inline codec: Codec[A], inline constraint: Constraint[A, C]): Codec[A :| C] =
codec.refined

val title: Codec[Title] = `enum`[Title](_.value, Title.apply, Type("title"))
val sex: Codec[Sex] = `enum`[Sex](_.value, Sex.apply, Type("sex"))
val ratingCodec: Codec[Rating] = int4.refined[RatingConstraint].imap(Rating.apply)(_.value)

val otherTitleArr: Codec[Arr[OtherTitle]] =
Codec.array(
Expand All @@ -111,7 +135,7 @@ private object Codecs:
val otherTitles: Codec[List[OtherTitle]] = otherTitleArr.opt.imap(_.fold(Nil)(_.toList))(Arr(_*).some)

val insertPlayer: Codec[InsertPlayer] =
(int4 *: text *: title.opt *: title.opt *: otherTitles *: int4.opt *: int4.opt *: int4.opt *: sex.opt *: int4.opt *: bool *: text.opt)
(int4 *: text *: title.opt *: title.opt *: otherTitles *: ratingCodec.opt *: ratingCodec.opt *: ratingCodec.opt *: sex.opt *: int4.opt *: bool *: text.opt)
.to[InsertPlayer]

val newFederation: Codec[NewFederation] =
Expand All @@ -127,31 +151,9 @@ private object Codecs:
(text *: text *: int4 *: stats *: stats *: stats).to[FederationSummary]

val playerInfo: Codec[PlayerInfo] =
(int4 *: text *: title.opt *: title.opt *: otherTitles *: int4.opt *: int4.opt *: int4.opt *: sex.opt *: int4.opt *: bool *: timestamptz *: timestamptz *: federationInfo.opt)
(int4 *: text *: title.opt *: title.opt *: otherTitles *: ratingCodec.opt *: ratingCodec.opt *: ratingCodec.opt *: sex.opt *: int4.opt *: bool *: timestamptz *: timestamptz *: federationInfo.opt)
.to[PlayerInfo]

// copy from https://github.com/Iltotore/iron/blob/main/skunk/src/io.github.iltotore.iron/skunk.scala for skunk 1.0.0
import io.github.iltotore.iron.*

/** Explicit conversion for refining a [[Codec]]. Decodes to the underlying type then checks the constraint.
*
* @param constraint
* the [[Constraint]] implementation to test the decoded value
*/
extension [A](codec: Codec[A])
inline def refined[C](using inline constraint: Constraint[A, C]): Codec[A :| C] =
codec.eimap[A :| C](_.refineEither[C])(_.asInstanceOf[A])

/** A [[Codec]] for refined types. Decodes to the underlying type then checks the constraint.
*
* @param codec
* the [[Codec]] of the underlying type
* @param constraint
* the [[Constraint]] implementation to test the decoded value
*/
inline given [A, C](using inline codec: Codec[A], inline constraint: Constraint[A, C]): Codec[A :| C] =
codec.refined

private object Sql:

import skunk.codec.all.*
Expand Down
14 changes: 7 additions & 7 deletions modules/db/src/test/scala/DbSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import cats.effect.kernel.Resource
import cats.syntax.all.*
import fide.domain.*
import fide.domain.Models.*
import fide.types.PositiveInt
import fide.types.*
import io.github.arainko.ducktape.*
import io.github.iltotore.iron.*
import org.typelevel.log4cats.Logger
Expand All @@ -28,9 +28,9 @@ object DbSuite extends SimpleIOSuite:
Title.GM.some,
Title.WGM.some,
List(OtherTitle.FI, OtherTitle.LSI),
2700.some,
2700.some,
2700.some,
Rating(2700).some,
Rating(2700).some,
Rating(2700).some,
Sex.Male.some,
1990.some,
true
Expand All @@ -42,9 +42,9 @@ object DbSuite extends SimpleIOSuite:
Title.GM.some,
Title.WGM.some,
List(OtherTitle.IA, OtherTitle.DI),
2700.some,
2700.some,
2700.some,
Rating(2700).some,
Rating(2700).some,
Rating(2700).some,
Sex.Female.some,
1990.some,
true
Expand Down
2 changes: 1 addition & 1 deletion modules/domain/src/main/scala/Domain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package fide
package domain

import cats.syntax.all.*
import fide.types.*

import java.time.OffsetDateTime

type PlayerId = Int
type Rating = Int
type FederationId = String

object FederationId:
Expand Down
14 changes: 14 additions & 0 deletions modules/types/src/main/scala/Rating.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package fide.types

import cats.syntax.all.*
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*

type RatingConstraint = GreaterEqual[1] & LessEqual[4000]
opaque type Rating <: Int = Int :| RatingConstraint

object Rating extends RefinedTypeOps[Int, RatingConstraint, Rating]:
def fromString(value: String): Option[Rating] =
value.toIntOption >>= Rating.option

extension (self: Rating) inline def toInt: Int = self

0 comments on commit e3cfa9c

Please sign in to comment.