Skip to content

Commit

Permalink
logging - scala3 support
Browse files Browse the repository at this point in the history
Co-authored-by: Vadim Chelyshov <[email protected]>
  • Loading branch information
Grryum and dos65 committed Mar 14, 2024
1 parent 0b62a0a commit f5c8ddb
Show file tree
Hide file tree
Showing 93 changed files with 736 additions and 300 deletions.
40 changes: 25 additions & 15 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import complete.DefaultParsers._
import sbt.librarymanagement.CrossVersion.{binaryScalaVersion, partialVersion}

lazy val scala2Versions = List(Version.scala212, Version.scala213)
lazy val scala2And3Versions = scala2Versions ::: List(Version.scala3)
lazy val scala3Versions = List(Version.scala3)
lazy val scala2And3Versions = scala2Versions ++ scala3Versions

val scopesDescription = s"Scala version can be: ${scala2And3Versions.mkString}"
val testScoped = inputKey[Unit](s"Run tests in the given scope. Usage: testScoped [scala version]. $scopesDescription")
Expand Down Expand Up @@ -58,12 +59,13 @@ lazy val higherKindCore = projectMatrix
defaultSettings,
scala3MigratedModuleOptions,
name := "tofu-core-higher-kind",
libraryDependencies ++= Seq(catsCore, catsFree, catsTaglessCore),
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) =>
Seq(catsCore, catsFree, catsTaglessMacros)
Seq(catsTaglessMacros)
case _ =>
Seq(catsCore, catsFree, catsTaglessCore)
Seq.empty
}
},
)
Expand Down Expand Up @@ -128,30 +130,38 @@ lazy val loggingStr = projectMatrix
.settings(
name := "tofu-logging-structured",
defaultSettings,
scala3MigratedModuleOptions,
libraryDependencies ++= Seq(
catsCore,
circeCore,
tethys,
tethysJackson,
// it is impossible to use tethys compiled for scala 3 until https://github.com/tethys-json/tethys/issues/266 is resolved
tethys cross CrossVersion.for3Use2_13,
tethysJackson cross CrossVersion.for3Use2_13,
slf4j,
alleycats,
scalatest,
derevo,
catsTaglessMacros
),
catsTaglessCore,
scalatest
)
)
.jvmPlatform(scala2Versions)
.jvmPlatform(scala2And3Versions)
.dependsOn(kernel)

lazy val loggingDer = projectMatrix
.in(modules / "logging" / "derivation")
.settings(
defaultSettings,
libraryDependencies ++= Seq(derevo, magnolia, slf4j, glassMacro % Test),
scala3MigratedModuleOptions,
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, _)) => Seq(derevo, magnolia2, slf4j, glassMacro % Test)
case Some((3, _)) => Seq(magnolia3, slf4j)
case _ => Seq.empty
}
},
name := "tofu-logging-derivation"
)
.jvmPlatform(scala2Versions)
.dependsOn(loggingStr, derivation % "compile->test")
.jvmPlatform(scala2And3Versions)
.dependsOn(loggingStr)

lazy val loggingLayout = projectMatrix
.in(modules / "logging" / "layout")
Expand Down Expand Up @@ -270,7 +280,7 @@ lazy val config = projectMatrix
.in(util / "config")
.settings(
defaultSettings,
libraryDependencies ++= Seq(typesafeConfig, magnolia, derevo, glassCore),
libraryDependencies ++= Seq(typesafeConfig, magnolia2, derevo, glassCore),
name := "tofu-config",
)
.jvmPlatform(scala2Versions)
Expand All @@ -290,7 +300,7 @@ lazy val derivation = projectMatrix
.in(modules / "derivation")
.settings(
defaultSettings,
libraryDependencies ++= Seq(magnolia, derevo, catsTaglessMacros),
libraryDependencies ++= Seq(magnolia2, derevo, catsTaglessMacros),
name := "tofu-derivation",
)
.jvmPlatform(scala2Versions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tofu.common.derived

import cats.Eval
import derevo.Derivation
import magnolia.{CaseClass, Magnolia, SealedTrait}
import magnolia1.{CaseClass, Magnolia, SealedTrait}
import tofu.common.Display

/** Derivation of [[Display]] typeclass for case classes and sealed traits
Expand All @@ -15,7 +15,7 @@ object display extends Derivation[Display] {

private type Typeclass[T] = Display[T]

def combine[T](ctx: CaseClass[Typeclass, T]): Display[T] = (cfg: Display.Config, a: T) => {
def join[T](ctx: CaseClass[Typeclass, T]): Display[T] = (cfg: Display.Config, a: T) => {
import cfg.{fieldSeparator, indent, brackets, fieldAssign, newline}

val nestedIndent = indent + indent
Expand Down Expand Up @@ -57,8 +57,8 @@ object display extends Derivation[Display] {
}
.map(s => s :+ brackets.right)
}
def dispatch[T](ctx: SealedTrait[Typeclass, T]): Display[T] = (cfg: Display.Config, a: T) =>
ctx.dispatch(a)(adtCase => adtCase.typeclass.displayBuild(cfg, adtCase.cast(a)))
def split[T](ctx: SealedTrait[Typeclass, T]): Display[T] = (cfg: Display.Config, a: T) =>
ctx.split(a)(adtCase => adtCase.typeclass.displayBuild(cfg, adtCase.cast(a)))

def instance[T]: Display[T] = macro Magnolia.gen[T]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package tofu.data.derived
import cats.Monad
import derevo.Derivation
import magnolia.{CaseClass, Magnolia}
import mercator.Monadic
import magnolia1.{CaseClass, Magnolia}
import magnolia1.Monadic
import tofu.Init

class InitDerivation[F[_]: Monad] extends Derivation[Init[F, *]] {
private[this] implicit val magnoliaMonad: Monadic[F] = new MerkatorFromCats[F]
private[this] implicit val magnolia1Monad: Monadic[F] = new MerkatorFromCats[F]

type Typeclass[A] = Init[F, A]

def combine[X](cc: CaseClass[Typeclass, X]): Init[F, X] =
def join[X](cc: CaseClass[Typeclass, X]): Init[F, X] =
new Init[F, X] {
def init: F[X] = cc.constructMonadic[F, Any](_.typeclass.init.asInstanceOf[F[Any]])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package derived

import java.time.{Instant, LocalDate, LocalDateTime, ZonedDateTime}
import cats.kernel.Semigroup
import magnolia.{CaseClass, Magnolia, SealedTrait}
import magnolia1.{CaseClass, Magnolia, SealedTrait}
import derevo.Derivation
import tofu.compat.unused

Expand All @@ -16,11 +16,11 @@ trait Merge[A] {
trait MergeInstances1 {
type Typeclass[A] = Merge[A]

def combine[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] =
def join[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] =
(a, b) => caseClass.construct(p => p.typeclass.merge(p.dereference(a), p.dereference(b)))

def dispatch[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] =
(a, b) => sealedTrait.dispatch(a) { h => if (h.cast.isDefinedAt(b)) h.typeclass.merge(h.cast(a), h.cast(b)) else a }
def split[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] =
(a, b) => sealedTrait.split(a) { h => if (h.cast.isDefinedAt(b)) h.typeclass.merge(h.cast(a), h.cast(b)) else a }

implicit def instance[A]: Merge[A] = macro Magnolia.gen[A]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package tofu.data.derived
import cats.Monad
import mercator.Monadic
import magnolia1.Monadic

class MerkatorFromCats[F[_]](implicit F: Monad[F]) extends Monadic[F] {
def point[A](value: A): F[A] = F.pure(value)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ trait Function2K[F[_], G[_], H[_]] {
}

object Function2K {
type HKAny[A] = Any

private[this] val representableAny = new Function2KRepresentable[HKAny, HKAny]

implicit def representableK[F[_], G[_]]: RepresentableK[({ type L[x[_]] = Function2K[F, G, x] })#L] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ package object higherKind {
type Pre[F[_], A] = Pre.T[F, A]

type UnitK[A] = Unit

type HKAny[_] = Any
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tofu.logging
package derivation

import cats.Show
import magnolia.{CaseClass, Magnolia, SealedTrait}
import magnolia1.{CaseClass, Magnolia, SealedTrait}
import derevo.Derivation
import derevo.NewTypeDerivation

Expand All @@ -12,7 +12,7 @@ object loggable extends Derivation[Loggable] with NewTypeDerivation[Loggable] {
def byShow[T: Show](name: String): Loggable[T] =
Loggable.stringValue.contramap(Show[T].show).named(name)

def combine[T](ctx: CaseClass[Typeclass, T]): Loggable[T] = new DictLoggable[T] {
def join[T](ctx: CaseClass[Typeclass, T]): Loggable[T] = new DictLoggable[T] {
private[this] val doNotShow = ctx.annotations.contains(hidden())

override val typeName: String = calcTypeName(ctx.typeName)
Expand All @@ -37,24 +37,24 @@ object loggable extends Derivation[Loggable] with NewTypeDerivation[Loggable] {

def logShow(value: T): String =
if (doNotShow) ""
else join(ctx.typeName.short, masking.params[Typeclass, T](value, ctx.parameters)(_.logShow))
else strJoin(ctx.typeName.short, masking.params[Typeclass, T](value, ctx.parameters)(_.logShow))
}

def dispatch[T](ctx: SealedTrait[Typeclass, T]): Loggable[T] = new Typeclass[T] {
def split[T](ctx: SealedTrait[Typeclass, T]): Loggable[T] = new Typeclass[T] {
override val typeName: String = calcTypeName(ctx.typeName)
override val shortName: String = ctx.typeName.short

def fields[I, V, R, M](a: T, input: I)(implicit receiver: LogRenderer[I, V, R, M]): R =
ctx.dispatch(a)(sub => sub.typeclass.fields(sub.cast(a), input))
ctx.split(a)(sub => sub.typeclass.fields(sub.cast(a), input))

def putValue[I, V, R, M](a: T, v: V)(implicit r: LogRenderer[I, V, R, M]): M =
ctx.dispatch(a)(sub => sub.typeclass.putValue(sub.cast(a), v))
ctx.split(a)(sub => sub.typeclass.putValue(sub.cast(a), v))

def logShow(a: T): String =
ctx.dispatch(a)(sub => sub.typeclass.logShow(sub.cast(a)))
ctx.split(a)(sub => sub.typeclass.logShow(sub.cast(a)))

override def putField[I, V, R, M](a: T, name: String, input: I)(implicit receiver: LogRenderer[I, V, R, M]): R =
ctx.dispatch(a)(sub => sub.typeclass.putField(sub.cast(a), name, input))
ctx.split(a)(sub => sub.typeclass.putField(sub.cast(a), name, input))
}

def instance[A]: Loggable[A] = macro Magnolia.gen[A]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package tofu.logging.derivation
import scala.annotation.tailrec
import scala.util.matching.Regex

import magnolia.Param
import magnolia1.Param

sealed trait MaskMode
object MaskMode {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package tofu.logging

import magnolia.TypeName
import magnolia1.TypeName
import scala.collection.compat._

package object derivation {
private[derivation] def join(typeName: String, strings: IterableOnce[String]): String =
private[derivation] def strJoin(typeName: String, strings: IterableOnce[String]): String =
if (strings.iterator.isEmpty) typeName else strings.iterator.mkString(s"$typeName{", ",", "}")

private[derivation] def calcTypeName(typeName: TypeName, seen: Set[TypeName] = Set()): String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tofu.logging.derivation

import cats.Show

import magnolia.{CaseClass, Magnolia, SealedTrait}
import magnolia1.{CaseClass, Magnolia, SealedTrait}
import derevo.Derivation
import derevo.NewTypeDerivation

Expand All @@ -17,15 +17,15 @@ object show extends Derivation[Show] with NewTypeDerivation[Show] {
/** creates a new [[Show]] instance by labelling and joining (with `mkString`) the result of showing each parameter,
* and prefixing it with the class name
*/
def combine[T](ctx: CaseClass[Typeclass, T]): Show[T] = value =>
def join[T](ctx: CaseClass[Typeclass, T]): Show[T] = value =>
if (ctx.isValueClass) {
val param = ctx.parameters.head
param.typeclass.show(param.dereference(value))
} else join(ctx.typeName.short, masking.params[Typeclass, T](value, ctx.parameters)(_.show))
} else strJoin(ctx.typeName.short, masking.params[Typeclass, T](value, ctx.parameters)(_.show))

/** choose which typeclass to use based on the subtype of the sealed trait */
def dispatch[T](ctx: SealedTrait[Typeclass, T]): Show[T] = value =>
ctx.dispatch(value)(sub => sub.typeclass.show(sub.cast(value)))
def split[T](ctx: SealedTrait[Typeclass, T]): Show[T] = value =>
ctx.split(value)(sub => sub.typeclass.show(sub.cast(value)))

def instance[T]: Show[T] = macro Magnolia.gen[T]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package tofu.logging
package derivation

import cats.Show
import magnolia1.*

object loggable extends AutoDerivation[Loggable] {

def byShow[T: Show](name: String): Loggable[T] =
Loggable.stringValue.contramap(Show[T].show).named(name)

def join[T](ctx: CaseClass[Typeclass, T]): Loggable[T] = new DictLoggable[T] {
private[this] val doNotShow = ctx.annotations.contains(hidden())

override val typeName: String = calcTypeName(ctx.typeInfo)
override val shortName: String = ctx.typeInfo.short

def fields[I, V, R, M](a: T, input: I)(implicit receiver: LogRenderer[I, V, R, M]): R =
ctx.parameters.iterator
.filter(!_.annotations.contains(hidden()))
.foldLeft(receiver.noop(input)) { (acc, param) =>
import param._

val value = deref(a)
typeclass match {
case _ if annotations.contains(unembed()) =>
receiver.combine(acc, typeclass.fields(value, input))
case _ =>
annotations.collectFirst { case masked(mode) =>
receiver.combine(acc, typeclass.putMaskedField(value, label, input)(masking.string(_, mode)))
}.getOrElse(receiver.combine(acc, typeclass.putField(value, label, input)))
}
}

def logShow(value: T): String =
if (doNotShow) ""
else strJoin(ctx.typeInfo.short, masking.params[Typeclass, T](value, ctx.parameters)(_.logShow))
}

def split[T](ctx: SealedTrait[Typeclass, T]): Loggable[T] = new Typeclass[T] {
override val typeName: String = calcTypeName(ctx.typeInfo)
override val shortName: String = ctx.typeInfo.short

def fields[I, V, R, M](a: T, input: I)(implicit receiver: LogRenderer[I, V, R, M]): R =
ctx.choose(a)(sub => sub.typeclass.fields(sub.cast(a), input))

def putValue[I, V, R, M](a: T, v: V)(implicit r: LogRenderer[I, V, R, M]): M =
ctx.choose(a)(sub => sub.typeclass.putValue(sub.cast(a), v))

def logShow(a: T): String =
ctx.choose(a)(sub => sub.typeclass.logShow(sub.cast(a)))

override def putField[I, V, R, M](a: T, name: String, input: I)(implicit receiver: LogRenderer[I, V, R, M]): R =
ctx.choose(a)(sub => sub.typeclass.putField(sub.cast(a), name, input))
}

}
Loading

0 comments on commit f5c8ddb

Please sign in to comment.