Skip to content

Commit

Permalink
Merge pull request #602 from gemini-hlsw/input-lenses
Browse files Browse the repository at this point in the history
Optics
  • Loading branch information
rpiaggio authored May 30, 2024
2 parents 95dc0db + 3676812 commit d0ac6bd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 0 deletions.
3 changes: 3 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ lazy val model =
Settings.Libraries.CatsTestkit.value ++
Settings.Libraries.Circe.value ++
Settings.Libraries.DisciplineMUnit.value ++
Settings.Libraries.Monocle.value ++
// Settings.Libraries.Discipline.value ++
Settings.Libraries.MonocleLaw.value ++
Settings.Libraries.MUnit.value
)

Expand Down
27 changes: 27 additions & 0 deletions model/src/main/scala/clue/data/InputOptics.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package clue.data

import monocle.PPrism
import monocle.Prism

trait InputOptics {
final def pAssign[A, B]: PPrism[Input[A], Input[B], A, B] =
PPrism[Input[A], Input[B], A, B] {
case Assign(a) => Right(a)
case Ignore => Left(Ignore)
case Unassign => Left(Unassign)
}(Assign.apply)

final def assign[A]: Prism[Input[A], A] =
pAssign[A, A]

final def ignore[A]: Prism[Input[A], Unit] =
Prism[Input[A], Unit] { case Ignore => Some(()); case _ => None }(_ => Ignore)

final def unassign[A]: Prism[Input[A], Unit] =
Prism[Input[A], Unit] { case Unassign => Some(()); case _ => None }(_ => Unassign)
}

object optics extends InputOptics
61 changes: 61 additions & 0 deletions model/src/main/scala/clue/data/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

package clue.data

import monocle.PLens
import monocle.POptional
import monocle.PPrism
import monocle.PTraversal

package object syntax {
implicit final class AnyToInputOps[A](private val a: A) extends AnyVal {
def assign: Input[A] = Assign(a)
Expand All @@ -12,4 +17,60 @@ package object syntax {
def orIgnore: Input[A] = Input.orIgnore(a)
def orUnassign: Input[A] = Input.orUnassign(a)
}

implicit final class LensToLensOps[S, T, A, B](private val lens: PLens[S, T, A, B])
extends AnyVal {
// Copied verbatim from monocle, which it is a private member.
private def adapt[A1, B1](implicit evA: A =:= A1, evB: B =:= B1): PLens[S, T, A1, B1] =
evB.substituteCo[PLens[S, T, A1, *]](evA.substituteCo[PLens[S, T, *, B]](lens))

def assign[A1, B1](implicit
ev1: A =:= Input[A1],
ev2: B =:= Input[B1]
): POptional[S, T, A1, B1] =
adapt[Input[A1], Input[B1]].andThen(optics.pAssign[A1, B1])
}

implicit final class PrismToPrismOps[S, T, A, B](private val prism: PPrism[S, T, A, B])
extends AnyVal {
// Copied verbatim from monocle, which it is a private member.
private def adapt[A1, B1](implicit evA: A =:= A1, evB: B =:= B1): PPrism[S, T, A1, B1] =
evB.substituteCo[PPrism[S, T, A1, *]](evA.substituteCo[PPrism[S, T, *, B]](prism))

def assign[A1, B1](implicit
ev1: A =:= Input[A1],
ev2: B =:= Input[B1]
): POptional[S, T, A1, B1] =
adapt[Input[A1], Input[B1]].andThen(optics.pAssign[A1, B1])
}

implicit final class OptionalToOptionalOps[S, T, A, B](
private val optional: POptional[S, T, A, B]
) extends AnyVal {
// Copied verbatim from monocle, which it is a private member.
private def adapt[A1, B1](implicit evA: A =:= A1, evB: B =:= B1): POptional[S, T, A1, B1] =
evB.substituteCo[POptional[S, T, A1, *]](evA.substituteCo[POptional[S, T, *, B]](optional))

def assign[A1, B1](implicit
ev1: A =:= Input[A1],
ev2: B =:= Input[B1]
): POptional[S, T, A1, B1] =
adapt[Input[A1], Input[B1]].andThen(optics.pAssign[A1, B1])
}

implicit final class TraversalToTraversalOps[S, T, A, B](
private val traversal: PTraversal[S, T, A, B]
) extends AnyVal {
// Copied verbatim from monocle, which it is a private member.
private def adapt[A1, B1](implicit evA: A =:= A1, evB: B =:= B1): PTraversal[S, T, A1, B1] =
evB.substituteCo[PTraversal[S, T, A1, *]](evA.substituteCo[PTraversal[S, T, *, B]](traversal))

def assign[A1, B1](implicit
ev1: A =:= Input[A1],
ev2: B =:= Input[B1]
): PTraversal[S, T, A1, B1] =
adapt[Input[A1], Input[B1]].andThen(optics.pAssign[A1, B1])

}

}
3 changes: 3 additions & 0 deletions model/src/test/scala/clue/data/InputSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.circe.*
import io.circe.generic.semiauto.*
import io.circe.testing.CodecTests
import io.circe.testing.instances.*
import monocle.law.discipline.PrismTests
import munit.DisciplineSuite
import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary.arbitrary
Expand Down Expand Up @@ -111,4 +112,6 @@ class InputSpec extends DisciplineSuite {
}

checkAll("SomeInput", CodecTests[SomeInput].codec)

checkAll("assign", PrismTests(optics.assign[Int]))
}
6 changes: 6 additions & 0 deletions project/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ object Settings {
)
)

val MonocleLaw = Def.setting(
Seq(
"dev.optics" %%% "monocle-law" % monocle
)
)

val MUnit = Def.setting(
Seq[ModuleID](
"org.scalameta" %%% "munit" % munit % "test"
Expand Down

0 comments on commit d0ac6bd

Please sign in to comment.