Skip to content

Commit

Permalink
Cross-build guava module on scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
RustedBones committed Nov 10, 2023
1 parent 074618a commit 0c12115
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 143 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:

- name: Build project
if: matrix.scala == '3'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' shared/test test/test scalacheck/test
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' shared/test test/test scalacheck/test guava/test

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ val scala3Cond = "matrix.scala == '3'"
val scala3Projects = List(
"shared",
"test",
"scalacheck"
"scalacheck",
"guava"
)
ThisBuild / scalaVersion := scalaDefault
ThisBuild / crossScalaVersions := Seq(scala3, scala213, scala212)
Expand Down Expand Up @@ -339,6 +340,7 @@ lazy val guava = project
commonSettings,
moduleName := "magnolify-guava",
description := "Magnolia add-on for Guava",
crossScalaVersions := Seq(scala3, scala213, scala212),
libraryDependencies ++= Seq(
"com.google.guava" % "guava" % guavaVersion % Provided
)
Expand Down
59 changes: 59 additions & 0 deletions guava/src/main/scala-2/magnolify/guava/FunnelDerivation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2019 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package magnolify.guava

import com.google.common.base.Charsets
import com.google.common.hash.{Funnel, PrimitiveSink}
import magnolia1.*

import scala.annotation.nowarn

object FunnelDerivation {
type Typeclass[T] = Funnel[T]

def join[T](caseClass: CaseClass[Funnel, T]): Funnel[T] = new Funnel[T] {
override def funnel(from: T, into: PrimitiveSink): Unit =
if (caseClass.isValueClass) {
val p = caseClass.parameters.head
p.typeclass.funnel(p.dereference(from), into)
} else if (caseClass.parameters.isEmpty) {
into.putString(caseClass.typeName.short, Charsets.UTF_8): @nowarn
} else {
caseClass.parameters.foreach { p =>
// inject index to distinguish cases like `(Some(1), None)` and `(None, Some(1))`
into.putInt(p.index)
p.typeclass.funnel(p.dereference(from), into)
}
}
}

def split[T](sealedTrait: SealedTrait[Funnel, T]): Funnel[T] = new Funnel[T] {
override def funnel(from: T, into: PrimitiveSink): Unit =
sealedTrait.split(from)(sub => sub.typeclass.funnel(sub.cast(from), into))
}

implicit def gen[T]: Funnel[T] = macro Magnolia.gen[T]

@deprecated("Use gen instead", "0.7.0")
implicit def apply[T]: Funnel[T] = macro Magnolia.gen[T]

@deprecated("Use funnel.contramap instead", "0.7.0")
def by[T, S](f: T => S)(implicit fnl: Funnel[S]): Funnel[T] = {
import FunnelImplicits.*
fnl.contramap(f)
}
}
23 changes: 23 additions & 0 deletions guava/src/main/scala-2/magnolify/guava/GuavaMacros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package magnolify.guava

import com.google.common.hash.Funnel

import scala.reflect.macros.*

object GuavaMacros {

def genFunnelMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe.*
val wtt = weakTypeTag[T]
q"""_root_.magnolify.guava.FunnelDerivation.gen[$wtt]"""
}

}

trait SemiAutoDerivations {
def genFunnel[T]: Funnel[T] = macro GuavaMacros.genFunnelMacro[T]
}

trait AutoDerivations {
implicit def genFunnel[T]: Funnel[T] = macro GuavaMacros.genFunnelMacro[T]
}
46 changes: 46 additions & 0 deletions guava/src/main/scala-3/magnolify/guava/FunnelDerivation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2019 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package magnolify.guava

import com.google.common.base.Charsets
import com.google.common.hash.{Funnel, PrimitiveSink}
import magnolia1.*

import scala.annotation.nowarn
import scala.deriving.Mirror

object FunnelDerivation extends Derivation[Funnel]:

def join[T](caseClass: CaseClass[Funnel, T]): Funnel[T] = new Funnel[T]:
override def funnel(from: T, into: PrimitiveSink): Unit =
if (caseClass.isValueClass)
val p = caseClass.parameters.head
p.typeclass.funnel(p.deref(from), into)
else if (caseClass.parameters.isEmpty)
into.putString(caseClass.typeInfo.short, Charsets.UTF_8): @nowarn
else
caseClass.parameters.foreach { p =>
// inject index to distinguish cases like `(Some(1), None)` and `(None, Some(1))`
into.putInt(p.index)
p.typeclass.funnel(p.deref(from), into)
}

def split[T](sealedTrait: SealedTrait[Funnel, T]): Funnel[T] = new Funnel[T]:
override def funnel(from: T, into: PrimitiveSink): Unit =
sealedTrait.choose(from)(sub => sub.typeclass.funnel(sub.cast(from), into))

inline def gen[T](using Mirror.Of[T]): Funnel[T] = derivedMirror[T]
11 changes: 11 additions & 0 deletions guava/src/main/scala-3/magnolify/guava/GuavaMacros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package magnolify.guava

import com.google.common.hash.Funnel

import scala.deriving.Mirror

trait SemiAutoDerivations:
inline def genFunnel[T](using Mirror.Of[T]): Funnel[T] = FunnelDerivation.derivedMirror[T]

trait AutoDerivations:
inline given genFunnel[T](using Mirror.Of[T]): Funnel[T] = FunnelDerivation.derivedMirror[T]
50 changes: 50 additions & 0 deletions guava/src/main/scala/magnolify/guava/FunnelImplicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package magnolify.guava

import com.google.common.hash.{Funnel, Funnels, PrimitiveSink}

import scala.annotation.nowarn
trait FunnelImplicits {
import FunnelImplicits.*

private def funnel[T](f: (PrimitiveSink, T) => Any): Funnel[T] = new Funnel[T] {
override def funnel(from: T, into: PrimitiveSink): Unit = f(into, from): @nowarn
}

implicit val intFunnel: Funnel[Int] = Funnels.integerFunnel().asInstanceOf[Funnel[Int]]
implicit val longFunnel: Funnel[Long] = Funnels.longFunnel().asInstanceOf[Funnel[Long]]
implicit val bytesFunnel: Funnel[Array[Byte]] = Funnels.byteArrayFunnel()
implicit val booleanFunnel: Funnel[Boolean] = funnel[Boolean](_.putBoolean(_))
implicit val byteFunnel: Funnel[Byte] = funnel[Byte](_.putByte(_))
implicit val charFunnel: Funnel[Char] = funnel[Char](_.putChar(_))
implicit val shortFunnel: Funnel[Short] = funnel[Short](_.putShort(_))

implicit def charSequenceFunnel[T <: CharSequence]: Funnel[T] =
Funnels.unencodedCharsFunnel().asInstanceOf[Funnel[T]]

// There is an implicit Option[T] => Iterable[T]
implicit def iterableFunnel[T, C[_]](implicit
fnl: Funnel[T],
ti: C[T] => Iterable[T]
): Funnel[C[T]] =
funnel { (sink, from) =>
var i = 0
ti(from).foreach { x =>
fnl.funnel(x, sink)
i += 1
}
// inject size to distinguish `None`, `Some("")`, and `List("", "", ...)`
sink.putInt(i)
}

def Funnel[T](implicit fnl: Funnel[T]): Funnel[T] = fnl

implicit def funnelOps[T](fnl: Funnel[T]): FunnelOps[T] = new FunnelOps(fnl)
}

object FunnelImplicits extends FunnelImplicits {
final class FunnelOps[T](val fnl: Funnel[T]) extends AnyVal {
def contramap[U](f: U => T): Funnel[U] = new Funnel[U] {
override def funnel(from: U, into: PrimitiveSink): Unit = fnl.funnel(f(from), into)
}
}
}
15 changes: 1 addition & 14 deletions guava/src/main/scala/magnolify/guava/auto/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,4 @@

package magnolify.guava

import com.google.common.hash.Funnel
import magnolify.guava.semiauto.FunnelImplicits

import scala.reflect.macros._

package object auto extends FunnelImplicits {
def genFunnelMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val wtt = weakTypeTag[T]
q"""_root_.magnolify.guava.semiauto.FunnelDerivation.apply[$wtt]"""
}

implicit def genFunnel[T]: Funnel[T] = macro genFunnelMacro[T]
}
package object auto extends FunnelImplicits with AutoDerivations

This file was deleted.

6 changes: 5 additions & 1 deletion guava/src/main/scala/magnolify/guava/semiauto/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@

package magnolify.guava

package object semiauto extends FunnelImplicits
package object semiauto extends FunnelImplicits with SemiAutoDerivations {

@deprecated("Use genFunnel[T] instead", "0.7.0")
val FunnelDerivation = magnolify.guava.FunnelDerivation
}
Loading

0 comments on commit 0c12115

Please sign in to comment.